From 251740d010a43966180404461d627aa4be635df0 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Wed, 12 Jul 2023 12:37:01 -0700 Subject: [PATCH 001/128] [fp-rs] Renaming folder windows_ble to windows, moving common to folder --- .../{common.rs => common/adapter.rs} | 10 ++------- fastpair/rust/src/bluetooth/common/device.rs | 21 +++++++++++++++++++ fastpair/rust/src/bluetooth/common/mod.rs | 20 ++++++++++++++++++ fastpair/rust/src/bluetooth/mod.rs | 4 ++-- .../{windows_ble => windows}/adapter.rs | 0 .../{windows_ble => windows}/device.rs | 0 .../bluetooth/{windows_ble => windows}/mod.rs | 0 7 files changed, 45 insertions(+), 10 deletions(-) rename fastpair/rust/src/bluetooth/{common.rs => common/adapter.rs} (80%) create mode 100644 fastpair/rust/src/bluetooth/common/device.rs create mode 100644 fastpair/rust/src/bluetooth/common/mod.rs rename fastpair/rust/src/bluetooth/{windows_ble => windows}/adapter.rs (100%) rename fastpair/rust/src/bluetooth/{windows_ble => windows}/device.rs (100%) rename fastpair/rust/src/bluetooth/{windows_ble => windows}/mod.rs (100%) diff --git a/fastpair/rust/src/bluetooth/common.rs b/fastpair/rust/src/bluetooth/common/adapter.rs similarity index 80% rename from fastpair/rust/src/bluetooth/common.rs rename to fastpair/rust/src/bluetooth/common/adapter.rs index 33ba2c4c..5e8be0ae 100644 --- a/fastpair/rust/src/bluetooth/common.rs +++ b/fastpair/rust/src/bluetooth/common/adapter.rs @@ -14,6 +14,8 @@ use async_trait::async_trait; +use super::Device; + /// Concrete types implementing this trait are Bluetooth Central devices. /// They provide methods for retrieving nearby connections and device info. #[async_trait] @@ -32,11 +34,3 @@ pub trait Adapter: Sized { /// Poll next discovered device. async fn next_device(&mut self) -> Result; } - -/// Concrete types implementing this trait represent Bluetooth Peripheral devices. -/// They provide methods for retrieving device info and running device actions, -/// such as pairing. -pub trait Device { - /// Retrieve the name advertised by this device. - fn name(&self) -> Result; -} diff --git a/fastpair/rust/src/bluetooth/common/device.rs b/fastpair/rust/src/bluetooth/common/device.rs new file mode 100644 index 00000000..1849fcef --- /dev/null +++ b/fastpair/rust/src/bluetooth/common/device.rs @@ -0,0 +1,21 @@ +// 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. + +/// Concrete types implementing this trait represent Bluetooth Peripheral devices. +/// They provide methods for retrieving device info and running device actions, +/// such as pairing. +pub trait Device { + /// Retrieve the name advertised by this device. + fn name(&self) -> Result; +} diff --git a/fastpair/rust/src/bluetooth/common/mod.rs b/fastpair/rust/src/bluetooth/common/mod.rs new file mode 100644 index 00000000..ea2fa401 --- /dev/null +++ b/fastpair/rust/src/bluetooth/common/mod.rs @@ -0,0 +1,20 @@ +// 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. + +/// Module for shared functionality between all Bluetooth platforms. +mod adapter; +mod device; + +pub use adapter::*; +pub use device::*; diff --git a/fastpair/rust/src/bluetooth/mod.rs b/fastpair/rust/src/bluetooth/mod.rs index 2faa2576..b49b95df 100644 --- a/fastpair/rust/src/bluetooth/mod.rs +++ b/fastpair/rust/src/bluetooth/mod.rs @@ -22,8 +22,8 @@ pub use common::{Adapter, Device}; cfg_if::cfg_if! { if #[cfg(windows)] { - mod windows_ble; - use windows_ble::BleAdapter; + mod windows; + use self::windows::BleAdapter; } else { mod unsupported; use unsupported::BleAdapter; diff --git a/fastpair/rust/src/bluetooth/windows_ble/adapter.rs b/fastpair/rust/src/bluetooth/windows/adapter.rs similarity index 100% rename from fastpair/rust/src/bluetooth/windows_ble/adapter.rs rename to fastpair/rust/src/bluetooth/windows/adapter.rs diff --git a/fastpair/rust/src/bluetooth/windows_ble/device.rs b/fastpair/rust/src/bluetooth/windows/device.rs similarity index 100% rename from fastpair/rust/src/bluetooth/windows_ble/device.rs rename to fastpair/rust/src/bluetooth/windows/device.rs diff --git a/fastpair/rust/src/bluetooth/windows_ble/mod.rs b/fastpair/rust/src/bluetooth/windows/mod.rs similarity index 100% rename from fastpair/rust/src/bluetooth/windows_ble/mod.rs rename to fastpair/rust/src/bluetooth/windows/mod.rs From 15d24c08a585dfe07436128cfcebce418c0a6c44 Mon Sep 17 00:00:00 2001 From: hai007 Date: Mon, 24 Jul 2023 10:58:06 -0700 Subject: [PATCH 002/128] Add sequence number for keep alive packet PiperOrigin-RevId: 550613263 --- connections/implementation/proto/offline_wire_formats.proto | 2 ++ 1 file changed, 2 insertions(+) diff --git a/connections/implementation/proto/offline_wire_formats.proto b/connections/implementation/proto/offline_wire_formats.proto index 018d4e18..d86743f6 100644 --- a/connections/implementation/proto/offline_wire_formats.proto +++ b/connections/implementation/proto/offline_wire_formats.proto @@ -310,6 +310,8 @@ message BandwidthUpgradeNegotiationFrame { message KeepAliveFrame { // And ack will be sent after receiving KEEP_ALIVE frame. optional bool ack = 1; + // The sequence number + optional uint32 seq_num = 2; } // Informs the remote side to immediately severe the socket connection. From 37b91196158dfa5349cecad29519a0dad0a43824 Mon Sep 17 00:00:00 2001 From: Anthony Rueda Date: Mon, 24 Jul 2023 13:47:47 -0700 Subject: [PATCH 003/128] [Presence] Extend shared credential proto with int64 secret id field PiperOrigin-RevId: 550661806 --- internal/proto/credential.proto | 5 ++++- internal/proto/local_credential.proto | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/internal/proto/credential.proto b/internal/proto/credential.proto index 5451d27b..f7236d48 100644 --- a/internal/proto/credential.proto +++ b/internal/proto/credential.proto @@ -43,7 +43,7 @@ enum CredentialType { // The shared credential is derived from local credential, and distributed to // remote devices based on the trust token for identity decryption and // authentication. -// NEXT_ID=14 +// NEXT_ID=15 // LINT.IfChange(SharedCredential) message SharedCredential { // The randomly generated unique id of the public credential. @@ -94,5 +94,8 @@ message SharedCredential { // The tag for verifying metadata_encryption_key for a signed V1 adv. bytes metadata_encryption_key_signed_adv_tag = 13; + + // The randomly generated positive unique id of the shared credential. + int64 id = 14; } // LINT.ThenChange(//depot/google3/google/internal/location/nearby/presence/v1/nearby_resources.proto:SharedCredential) diff --git a/internal/proto/local_credential.proto b/internal/proto/local_credential.proto index 29844c15..7fbcada0 100644 --- a/internal/proto/local_credential.proto +++ b/internal/proto/local_credential.proto @@ -28,6 +28,7 @@ option optimize_for = LITE_RUNTIME; // The local credential contains information of a local device for // identity encryption and authentication. It should never leave the generating // device. +// NEXT_ID=11 message LocalCredential { // Private encryption key descriptor. // Usually, either `certificate_alias` or `key` is set. @@ -71,4 +72,8 @@ message LocalCredential { // The 16 bytes aes key to encrypt metadata in PublicCredential. bytes metadata_encryption_key_v1 = 10; + + // The positive unique id of (and hashed based on) a pair of Secret Key and + // X509Certificate's public key. + int64 id = 11; } From 99c5e19f3f41f224457512a7ca646b940926e004 Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Mon, 24 Jul 2023 18:14:47 -0700 Subject: [PATCH 004/128] internal update PiperOrigin-RevId: 550729573 --- .../platform/implementation/windows/timer.cc | 25 ++++++--------- .../platform/implementation/windows/timer.h | 8 ++--- .../implementation/windows/timer_test.cc | 31 +++++-------------- 3 files changed, 21 insertions(+), 43 deletions(-) diff --git a/internal/platform/implementation/windows/timer.cc b/internal/platform/implementation/windows/timer.cc index c4e35241..c27846ef 100644 --- a/internal/platform/implementation/windows/timer.cc +++ b/internal/platform/implementation/windows/timer.cc @@ -1,4 +1,4 @@ -// Copyright 2021-2023 Google LLC +// 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. @@ -14,9 +14,10 @@ #include "internal/platform/implementation/windows/timer.h" -#include "absl/functional/any_invocable.h" +#include + +#include "absl/synchronization/mutex.h" #include "internal/platform/logging.h" -#include "internal/platform/mutex_lock.h" namespace nearby { namespace windows { @@ -25,7 +26,7 @@ Timer::~Timer() { Stop(); } bool Timer::Create(int delay, int interval, absl::AnyInvocable callback) { - MutexLock lock(&mutex_); + absl::MutexLock lock(&mutex_); if ((delay < 0) || (interval < 0)) { NEARBY_LOGS(WARNING) << "Delay and interval shouldn\'t be negative value."; @@ -45,17 +46,10 @@ bool Timer::Create(int delay, int interval, delay_ = delay; interval_ = interval; callback_ = std::move(callback); - timer_callback_ = [&]() { - MutexLock lock(&mutex_); - if (timer_queue_handle_ != nullptr && callback_ != nullptr) { - callback_(); - } - }; if (!CreateTimerQueueTimer(&handle_, timer_queue_handle_, static_cast(TimerRoutine), - &timer_callback_, delay, interval, - WT_EXECUTEDEFAULT)) { + &callback_, delay, interval, WT_EXECUTEDEFAULT)) { if (!DeleteTimerQueueEx(timer_queue_handle_, nullptr)) { NEARBY_LOGS(ERROR) << "Failed to create timer in timer queue."; } @@ -67,7 +61,7 @@ bool Timer::Create(int delay, int interval, } bool Timer::Stop() { - MutexLock lock(&mutex_); + absl::MutexLock lock(&mutex_); if (timer_queue_handle_ == nullptr) { return true; @@ -92,7 +86,7 @@ bool Timer::Stop() { } bool Timer::FireNow() { - MutexLock lock(&mutex_); + absl::MutexLock lock(&mutex_); if (!timer_queue_handle_ || !callback_) { return false; @@ -107,7 +101,8 @@ bool Timer::FireNow() { << "Failed to fire the task due to cannot create executor."; return false; } - task_executor_->Execute([&]() { timer_callback_(); }); + + task_executor_->Execute([&]() { callback_(); }); return true; } diff --git a/internal/platform/implementation/windows/timer.h b/internal/platform/implementation/windows/timer.h index fe2d2ae9..8b9d9806 100644 --- a/internal/platform/implementation/windows/timer.h +++ b/internal/platform/implementation/windows/timer.h @@ -1,4 +1,4 @@ -// Copyright 2021-2023 Google LLC +// 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. @@ -20,10 +20,9 @@ #include #include "absl/base/thread_annotations.h" -#include "absl/functional/any_invocable.h" +#include "absl/synchronization/mutex.h" #include "internal/platform/implementation/timer.h" #include "internal/platform/implementation/windows/submittable_executor.h" -#include "internal/platform/mutex.h" namespace nearby { namespace windows { @@ -42,11 +41,10 @@ class Timer : public api::Timer { private: static void CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired); - mutable RecursiveMutex mutex_; + mutable absl::Mutex mutex_; int delay_ ABSL_GUARDED_BY(mutex_); int interval_ ABSL_GUARDED_BY(mutex_); absl::AnyInvocable callback_; - absl::AnyInvocable timer_callback_ = nullptr; HANDLE handle_ ABSL_GUARDED_BY(mutex_) = nullptr; HANDLE timer_queue_handle_ ABSL_GUARDED_BY(mutex_) = nullptr; std::unique_ptr task_executor_ ABSL_GUARDED_BY(mutex_) = diff --git a/internal/platform/implementation/windows/timer_test.cc b/internal/platform/implementation/windows/timer_test.cc index be3e7964..725397cd 100644 --- a/internal/platform/implementation/windows/timer_test.cc +++ b/internal/platform/implementation/windows/timer_test.cc @@ -1,4 +1,4 @@ -// Copyright 2021-2023 Google LLC +// 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. @@ -14,8 +14,12 @@ #include "internal/platform/implementation/timer.h" +#include // NOLINT +// NOLINT +#include +#include // NOLINT + #include "gtest/gtest.h" -#include "absl/time/time.h" #include "internal/platform/implementation/platform.h" namespace nearby { @@ -42,7 +46,7 @@ TEST(Timer, DISABLED_TestRepeatTimer) { ASSERT_TRUE(timer != nullptr); EXPECT_TRUE(timer->Create(300, 300, [&]() { ++count; })); - absl::SleepFor(absl::Seconds(1)); + std::this_thread::sleep_for(std::chrono::seconds(1)); EXPECT_TRUE(timer->Stop()); EXPECT_EQ(count, 3); } @@ -53,27 +57,8 @@ TEST(Timer, DISABLED_TestFireNow) { auto timer = nearby::api::ImplementationPlatform::CreateTimer(); EXPECT_TRUE(timer != nullptr); - EXPECT_TRUE(timer->Create(3000, 3000, [&]() { - absl::SleepFor(absl::Milliseconds(1000)); - ++count; - })); + EXPECT_TRUE(timer->Create(3000, 3000, [&]() { ++count; })); EXPECT_TRUE(timer->FireNow()); - absl::SleepFor(absl::Milliseconds(100)); - EXPECT_TRUE(timer->Stop()); - EXPECT_EQ(count, 1); -} - -TEST(Timer, DISABLED_TestWaitForRunningCallback) { - int count = 0; - - auto timer = nearby::api::ImplementationPlatform::CreateTimer(); - - EXPECT_TRUE(timer != nullptr); - EXPECT_TRUE(timer->Create(1000, 0, [&]() { - absl::SleepFor(absl::Milliseconds(3000)); - ++count; - })); - absl::SleepFor(absl::Milliseconds(1050)); EXPECT_TRUE(timer->Stop()); EXPECT_EQ(count, 1); } From 5b605b24267795eba840ec0daacc374deacfeaa4 Mon Sep 17 00:00:00 2001 From: Joy Babafemi Date: Tue, 25 Jul 2023 12:15:06 -0700 Subject: [PATCH 005/128] Refactor std::function usage to absl::AnyInvocable for ZoneTransitionCallback PiperOrigin-RevId: 550959839 --- presence/fpp/BUILD | 1 + presence/fpp/fpp_manager.cc | 11 ++++--- presence/fpp/fpp_manager.h | 1 - presence/fpp/fpp_manager_test.cc | 41 +++++++++++++++---------- presence/implementation/BUILD | 1 + presence/implementation/sensor_fusion.h | 18 +++++++---- 6 files changed, 46 insertions(+), 27 deletions(-) diff --git a/presence/fpp/BUILD b/presence/fpp/BUILD index f551c07b..2f12acb7 100644 --- a/presence/fpp/BUILD +++ b/presence/fpp/BUILD @@ -40,6 +40,7 @@ cc_test( 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", diff --git a/presence/fpp/fpp_manager.cc b/presence/fpp/fpp_manager.cc index a70265a4..f0ce1ec2 100644 --- a/presence/fpp/fpp_manager.cc +++ b/presence/fpp/fpp_manager.cc @@ -17,10 +17,12 @@ #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 { @@ -110,7 +112,7 @@ absl::Status FppManager::UpdateBleScanResult(uint64_t device_id, void FppManager::RegisterZoneTransitionListener( uint64_t callback_id, ZoneTransitionCallback callback) { - zone_transition_callbacks_[callback_id] = callback; + zone_transition_callbacks_[callback_id] = std::move(callback); } void FppManager::UnregisterZoneTransitionListener(uint64_t callback_id) { @@ -147,9 +149,10 @@ void FppManager::CheckPresenceZoneChanged(uint64_t device_id, NEARBY_LOG(WARNING, "Updating zone transition callbacks with new zone. Zone=%p", new_estimate.proximity_state); - for (const auto& pair : zone_transition_callbacks_) { - pair.second(device_id, ConvertProximityStateToRangeType( - new_estimate.proximity_state)); + for (auto& pair : zone_transition_callbacks_) { + pair.second.on_proximity_zone_changed( + device_id, + ConvertProximityStateToRangeType(new_estimate.proximity_state)); } } } diff --git a/presence/fpp/fpp_manager.h b/presence/fpp/fpp_manager.h index 50b616af..d13e4adb 100644 --- a/presence/fpp/fpp_manager.h +++ b/presence/fpp/fpp_manager.h @@ -31,7 +31,6 @@ namespace presence { // between fpp and NP sensor fusion class FppManager { public: - using ZoneTransitionCallback = SensorFusion::ZoneTransitionCallback; using RangeType = PresenceZone::DistanceBoundary::RangeType; FppManager() { presence_detector_handle_ = presence_detector_create(); } diff --git a/presence/fpp/fpp_manager_test.cc b/presence/fpp/fpp_manager_test.cc index b7ed5e72..76dc2fc2 100644 --- a/presence/fpp/fpp_manager_test.cc +++ b/presence/fpp/fpp_manager_test.cc @@ -19,6 +19,7 @@ #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "presence/implementation/sensor_fusion.h" namespace nearby { namespace presence { @@ -33,10 +34,12 @@ TEST(FppManager, UpdateBleScanResultSuccess) { bool callback_called = false; manager.RegisterZoneTransitionListener( kCallbackId, - [&callback_called](uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - callback_called = true; - }); + {.on_proximity_zone_changed = + [&callback_called]( + uint64_t device_id, + PresenceZone::DistanceBoundary::RangeType range_type) { + callback_called = true; + }}); EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, kReachRssi, /*elapsed_real_time_millis=*/0)); @@ -56,10 +59,12 @@ TEST(FppManager, ZoneTransitionDetected) { bool callback_called = false; manager.RegisterZoneTransitionListener( kCallbackId, - [&callback_called](uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - callback_called = true; - }); + {.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=*/absl::nullopt, kReachRssi, @@ -139,10 +144,12 @@ TEST(FppManager, UpdateBleScanResultWithTxPowerSuccess) { bool callback_called = false; manager.RegisterZoneTransitionListener( kCallbackId, - [&callback_called](uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - callback_called = true; - }); + {.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, @@ -159,10 +166,12 @@ TEST(FppManager, UnregisterZoneTransitionListener) { bool callback_called = false; manager.RegisterZoneTransitionListener( kCallbackId, - [&callback_called](uint64_t device_id, - PresenceZone::DistanceBoundary::RangeType range_type) { - callback_called = true; - }); + {.on_proximity_zone_changed = + [&callback_called]( + uint64_t device_id, + PresenceZone::DistanceBoundary::RangeType range_type) { + callback_called = true; + }}); EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, kReachRssi, /*elapsed_real_time_millis=*/0)); diff --git a/presence/implementation/BUILD b/presence/implementation/BUILD index 97fc31b2..a3da0f81 100644 --- a/presence/implementation/BUILD +++ b/presence/implementation/BUILD @@ -126,6 +126,7 @@ cc_library( ], deps = [ "//presence:types", + "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/types:optional", ], ) diff --git a/presence/implementation/sensor_fusion.h b/presence/implementation/sensor_fusion.h index 452b01fd..027f2225 100644 --- a/presence/implementation/sensor_fusion.h +++ b/presence/implementation/sensor_fusion.h @@ -19,6 +19,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "absl/types/optional.h" #include "presence/device_motion.h" #include "presence/presence_zone.h" @@ -58,16 +59,21 @@ struct RangingData { 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 the proximity zone to a nearby peer device has changed. - typedef std::function - ZoneTransitionCallback; - // Called when a device motion gesture is detected. typedef std::function DeviceMotionCallback; From ae69b8ff4eaf74f4f881049df162789403f4b39f Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Tue, 25 Jul 2023 14:03:14 -0700 Subject: [PATCH 006/128] Protect PendingPayload from use-after-free errors. Protects against race conditions where a PendingPayload is destroyed when another thread is still accessing it. PiperOrigin-RevId: 550990038 --- connections/implementation/BUILD | 2 + connections/implementation/payload_manager.cc | 331 ++++++++++-------- connections/implementation/payload_manager.h | 120 +++++-- 3 files changed, 288 insertions(+), 165 deletions(-) diff --git a/connections/implementation/BUILD b/connections/implementation/BUILD index 187965ba..bb8eb3e5 100644 --- a/connections/implementation/BUILD +++ b/connections/implementation/BUILD @@ -137,7 +137,9 @@ cc_library( "@com_google_absl//absl/container:btree", "@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/functional:bind_front", + "@com_google_absl//absl/log:check", "@com_google_absl//absl/memory", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", diff --git a/connections/implementation/payload_manager.cc b/connections/implementation/payload_manager.cc index fe42ce23..a01b0a64 100644 --- a/connections/implementation/payload_manager.cc +++ b/connections/implementation/payload_manager.cc @@ -22,8 +22,11 @@ #include #include +#include "absl/functional/any_invocable.h" +#include "absl/functional/bind_front.h" #include "absl/memory/memory.h" #include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "absl/time/time.h" #include "connections/implementation/analytics/throughput_recorder.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" @@ -279,9 +282,11 @@ Payload::Id PayloadManager::CreateOutgoingPayload( NEARBY_LOGS(INFO) << "CreateOutgoingPayload: payload_id=" << payload_id; MutexLock lock(&mutex_); pending_payloads_.StartTrackingPayload( - payload_id, absl::make_unique(std::move(internal_payload), - endpoint_ids, - /*is_incoming=*/false)); + payload_id, + std::make_unique( + std::move(internal_payload), endpoint_ids, + /*is_incoming=*/false, + absl::bind_front(&PayloadManager::OnPendingPayloadDestroy, this))); return payload_id; } @@ -297,18 +302,17 @@ void PayloadManager::CancelAllPayloads() { { MutexLock lock(&mutex_); int pending_outgoing_payloads = 0; - for (const auto& pending_id : pending_payloads_.GetAllPayloads()) { - auto* pending = pending_payloads_.GetPayload(pending_id); + pending_payloads_.ForEachPayload([&](PendingPayload* pending) { if (!pending->IsIncoming()) pending_outgoing_payloads++; pending->MarkLocallyCanceled(); pending->Close(); // To unblock the sender thread, if there is no data. - } + }); + if (pending_outgoing_payloads) { shutdown_barrier_ = absl::make_unique(pending_outgoing_payloads); } } - if (shutdown_barrier_) { NEARBY_LOG(INFO, "PayloadManager: waiting for pending outgoing payloads; self=%p", @@ -342,9 +346,7 @@ PayloadManager::~PayloadManager() { NEARBY_LOG(INFO, "PayloadManager: stop tracking payloads; self=%p", this); MutexLock lock(&mutex_); - for (const auto& pending_id : pending_payloads_.GetAllPayloads()) { - pending_payloads_.StopTrackingPayload(pending_id); - } + pending_payloads_.StopTrackingAllPayloads(); stop_latch.CountDown(); }); stop_latch.Await(); @@ -419,7 +421,7 @@ void PayloadManager::SendPayload(ClientProxy* client, "send-payload", [this, client, endpoint_ids, payload_id, payload_type, resume_offset, payload_total_size]() { if (shutdown_.Get()) return; - PendingPayload* pending_payload = GetPayload(payload_id); + PendingPayloadHandle pending_payload = GetPayload(payload_id); if (!pending_payload) { RecordInvalidPayloadAnalytics(client, endpoint_ids, payload_id, payload_type, resume_offset, @@ -468,15 +470,14 @@ void PayloadManager::SendPayload(ClientProxy* client, << ", payload_type=" << ToString(payload_type); } -PayloadManager::PendingPayload* PayloadManager::GetPayload( +PayloadManager::PendingPayloadHandle PayloadManager::GetPayload( Payload::Id payload_id) const { - MutexLock lock(&mutex_); return pending_payloads_.GetPayload(payload_id); } Status PayloadManager::CancelPayload(ClientProxy* client, Payload::Id payload_id) { - PendingPayload* canceled_payload = GetPayload(payload_id); + PendingPayloadHandle canceled_payload = GetPayload(payload_id); if (!canceled_payload) { NEARBY_LOGS(INFO) << "Client requested cancel for unknown payload_id=" << payload_id << ", ignoring."; @@ -532,51 +533,49 @@ void PayloadManager::OnEndpointDisconnect(ClientProxy* client, } RunOnStatusUpdateThread( "payload-manager-on-disconnect", - [this, client, endpoint_id, barrier]() - RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() mutable { - // Iterate through all our payloads and look for payloads associated - // with this endpoint. - MutexLock lock(&mutex_); - for (const auto& payload_id : pending_payloads_.GetAllPayloads()) { - auto* pending_payload = pending_payloads_.GetPayload(payload_id); - if (!pending_payload) continue; - auto endpoint_info = pending_payload->GetEndpoint(endpoint_id); - if (!endpoint_info) continue; - std::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; + [this, client, endpoint_id, + barrier]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() mutable { + // Iterate through all our payloads and look for payloads associated + // with this endpoint. + MutexLock lock(&mutex_); + 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; + // 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 = - pending_payload->GetInternalPayload()->GetTotalSize(); + std::int64_t payload_total_size = + pending_payload->GetInternalPayload()->GetTotalSize(); - // If no endpoints are left for this payload, close it. - if (pending_payload->GetEndpoints().empty()) { - pending_payload->Close(); - } + // If no endpoints are left for this payload, close it. + if (pending_payload->GetEndpoints().empty()) { + pending_payload->Close(); + } + // Create the payload transfer update. + PayloadProgressInfo update{pending_payload->GetId(), + PayloadProgressInfo::Status::kFailure, + payload_total_size, endpoint_offset}; - // Create the payload transfer update. - PayloadProgressInfo update{payload_id, - PayloadProgressInfo::Status::kFailure, - payload_total_size, endpoint_offset}; + // Send a client notification of a payload transfer failure. + client->OnPayloadProgress(endpoint_id, update); - // Send a client notification of a payload transfer failure. - client->OnPayloadProgress(endpoint_id, update); + if (pending_payload->IsIncoming()) { + client->GetAnalyticsRecorder().OnIncomingPayloadDone( + endpoint_id, pending_payload->GetId(), + location::nearby::proto::connections::ENDPOINT_IO_ERROR); + } else { + client->GetAnalyticsRecorder().OnOutgoingPayloadDone( + endpoint_id, pending_payload->GetId(), + location::nearby::proto::connections::ENDPOINT_IO_ERROR); + } + }); - if (pending_payload->IsIncoming()) { - client->GetAnalyticsRecorder().OnIncomingPayloadDone( - endpoint_id, pending_payload->GetId(), - location::nearby::proto::connections::ENDPOINT_IO_ERROR); - } else { - client->GetAnalyticsRecorder().OnOutgoingPayloadDone( - endpoint_id, pending_payload->GetId(), - location::nearby::proto::connections::ENDPOINT_IO_ERROR); - } - } - - barrier.CountDown(); - }); + barrier.CountDown(); + }); } location::nearby::proto::connections::PayloadStatus @@ -686,25 +685,33 @@ PayloadTransferFrame::PayloadChunk PayloadManager::CreatePayloadChunk( return payload_chunk; } -PayloadManager::PendingPayload* PayloadManager::CreateIncomingPayload( +PayloadManager::PendingPayloadHandle PayloadManager::CreateIncomingPayload( const PayloadTransferFrame& frame, const std::string& endpoint_id) { auto internal_payload = CreateIncomingInternalPayload(frame, custom_save_path_); if (!internal_payload) { - return nullptr; + return PendingPayloadHandle(); } Payload::Id payload_id = internal_payload->GetId(); NEARBY_LOGS(INFO) << "CreateIncomingPayload: payload_id=" << payload_id; - MutexLock lock(&mutex_); pending_payloads_.StartTrackingPayload( payload_id, - absl::make_unique(std::move(internal_payload), - EndpointIds{endpoint_id}, true)); - + std::make_unique( + std::move(internal_payload), EndpointIds{endpoint_id}, true, + absl::bind_front(&PayloadManager::OnPendingPayloadDestroy, this))); return pending_payloads_.GetPayload(payload_id); } +void PayloadManager::OnPendingPayloadDestroy(const PendingPayload* payload) { + NEARBY_LOGS(INFO) << "PayloadManager: destroying " << payload->ToString() + << " self=" << this; + if (payload->IsIncoming()) return; + RunOnStatusUpdateThread( + "~PendingPayload", + [this]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() { NotifyShutdown(); }); +} + void PayloadManager::SendClientCallbacksForFinishedOutgoingPayload( ClientProxy* client, const EndpointIds& finished_endpoint_ids, const PayloadTransferFrame::PayloadHeader& payload_header, @@ -716,7 +723,7 @@ void PayloadManager::SendClientCallbacksForFinishedOutgoingPayload( num_bytes_successfully_transferred, status]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() { // Make sure we're still tracking this payload. - PendingPayload* pending_payload = GetPayload(payload_header.id()); + PendingPayloadHandle pending_payload = GetPayload(payload_header.id()); if (!pending_payload) { return; } @@ -760,13 +767,14 @@ void PayloadManager::SendClientCallbacksForFinishedIncomingPayload( [this, client, endpoint_id, payload_header, offset_bytes, status]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() { // Make sure we're still tracking this payload. - PendingPayload* pending_payload = GetPayload(payload_header.id()); + PendingPayloadHandle pending_payload = GetPayload(payload_header.id()); if (!pending_payload) { return; } - // Unless we never started tracking this payload (meaning we failed to - // even create the InternalPayload), notify the client (and close it). + // 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), @@ -908,7 +916,7 @@ void PayloadManager::HandleSuccessfulOutgoingChunk( } } - PendingPayload* pending_payload = GetPayload(payload_header.id()); + PendingPayloadHandle pending_payload = GetPayload(payload_header.id()); if (!pending_payload || !pending_payload->GetEndpoint(endpoint_id)) { NEARBY_LOGS(INFO) << "HandleSuccessfulOutgoingChunk: endpoint not found: " @@ -949,20 +957,7 @@ void PayloadManager::HandleSuccessfulOutgoingChunk( // @PayloadManagerStatusUpdateThread void PayloadManager::DestroyPendingPayload(Payload::Id payload_id) { - bool is_incoming = false; - { - MutexLock lock(&mutex_); - auto pending = pending_payloads_.StopTrackingPayload(payload_id); - if (!pending) return; - is_incoming = pending->IsIncoming(); - const char* direction = is_incoming ? "incoming" : "outgoing"; - NEARBY_LOGS(INFO) << "PayloadManager: destroying " << direction - << " pending payload: self=" << this - << "; payload_id=" << payload_id; - pending->Close(); - pending.reset(); - } - if (!is_incoming) NotifyShutdown(); + pending_payloads_.StopTrackingPayload(payload_id); } void PayloadManager::HandleSuccessfulIncomingChunk( @@ -1003,7 +998,7 @@ void PayloadManager::HandleSuccessfulIncomingChunk( } } - PendingPayload* pending_payload = GetPayload(payload_header.id()); + PendingPayloadHandle pending_payload = GetPayload(payload_header.id()); if (!pending_payload) { return; } @@ -1044,11 +1039,11 @@ void PayloadManager::ProcessDataPacket( << payload_header.id() << " from endpoint_id=" << from_endpoint_id << " at offset " << payload_chunk.offset(); - - PendingPayload* pending_payload; + Payload::Id payload_id = payload_header.id(); + PendingPayloadHandle pending_payload; if (payload_chunk.offset() == 0) { ThroughputRecorderContainer::GetInstance() - .GetTPRecorder(payload_header.id(), PayloadDirection::INCOMING_PAYLOAD) + .GetTPRecorder(payload_id, PayloadDirection::INCOMING_PAYLOAD) ->Start((PayloadType)payload_header.type(), PayloadDirection::INCOMING_PAYLOAD); packet_meta_data.Reset(); @@ -1077,12 +1072,13 @@ void PayloadManager::ProcessDataPacket( PayloadTransferFrame::ControlMessage::PAYLOAD_ERROR); return; } - // Also, let the client know of this new incoming payload. RunOnStatusUpdateThread( "process-data-packet", - [to_client, from_endpoint_id, pending_payload]() + [to_client, from_endpoint_id, + pending_payload = GetPayload(payload_id)]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() { + if (!pending_payload) return; NEARBY_LOGS(INFO) << "PayloadManager received new payload_id=" << pending_payload->GetInternalPayload()->GetId() @@ -1093,17 +1089,17 @@ void PayloadManager::ProcessDataPacket( }); } else { pending_payload = GetPayload(payload_header.id()); - if (!pending_payload) { - NEARBY_LOGS(WARNING) << "ProcessDataPacket: [missing] endpoint_id=" - << from_endpoint_id - << "; payload_id=" << payload_header.id(); - return; - } } + if (!pending_payload) { + NEARBY_LOGS(WARNING) << "ProcessDataPacket: [missing] endpoint_id=" + << from_endpoint_id + << "; payload_id=" << payload_header.id(); + return; + } if (pending_payload->IsLocallyCanceled()) { - // This incoming payload was canceled by the client. Drop this frame and do - // all the cleanup. See go/nc-cancel-payload + // This incoming payload was canceled by the client. Drop this frame and + // do all the cleanup. See go/nc-cancel-payload NEARBY_LOGS(INFO) << "ProcessDataPacket: [cancel] endpoint_id=" << from_endpoint_id << "; payload_id=" << pending_payload->GetId(); @@ -1115,10 +1111,10 @@ void PayloadManager::ProcessDataPacket( } // Update the offset for this payload. An endpoint disconnection might occur - // from another thread and we would need to know the current offset to report - // back to the client. 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 attaching the next chunk. + // from another thread and we would need to know the current offset to + // report back to the client. 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 attaching the next chunk. pending_payload->SetOffsetForEndpoint(from_endpoint_id, payload_chunk.offset()); @@ -1165,7 +1161,7 @@ void PayloadManager::ProcessControlPacket( payload_transfer_frame.payload_header(); const PayloadTransferFrame::ControlMessage& control_message = payload_transfer_frame.control_message(); - PendingPayload* pending_payload = GetPayload(payload_header.id()); + PendingPayloadHandle pending_payload = GetPayload(payload_header.id()); if (!pending_payload) { NEARBY_LOGS(INFO) << "Got ControlMessage for unknown payload_id=" << payload_header.id() @@ -1266,7 +1262,8 @@ void PayloadManager::SetCustomSavePath(ClientProxy* client, custom_save_path_ = path; } -///////////////////////////////// EndpointInfo ///////////////////////////////// +///////////////////////////////// EndpointInfo +//////////////////////////////////// PayloadManager::EndpointInfo::Status PayloadManager::EndpointInfo::ControlMessageEventToEndpointInfoStatus( @@ -1292,13 +1289,16 @@ void PayloadManager::EndpointInfo::SetStatusFromControlMessage( << " based on OOB ControlMessage"; } -//////////////////////////////// PendingPayload //////////////////////////////// +//////////////////////////////// PendingPayload +/////////////////////////////////// PayloadManager::PendingPayload::PendingPayload( std::unique_ptr internal_payload, - const EndpointIds& endpoint_ids, bool is_incoming) + const EndpointIds& endpoint_ids, bool is_incoming, + DestroyCallback destroy_callback) : is_incoming_(is_incoming), - internal_payload_(std::move(internal_payload)) { + internal_payload_(std::move(internal_payload)), + destroy_callback_(std::move(destroy_callback)) { // Initially we mark all endpoints as available. // Later on some may become canceled, some may experience data transfer // failures. Any of these situations will cause endpoint to be marked as @@ -1384,66 +1384,123 @@ void PayloadManager::PendingPayload::SetOffsetForEndpoint( } void PayloadManager::PendingPayload::Close() { + bool was_closed = is_closed_.Set(true); + if (was_closed) return; if (internal_payload_) internal_payload_->Close(); - close_event_.CountDown(); } -bool PayloadManager::PendingPayload::WaitForClose() { - return close_event_.Await(kWaitCloseTimeout).result(); -} - -bool PayloadManager::PendingPayload::IsClosed() { - return close_event_.Await(absl::ZeroDuration()).result(); -} - -void PayloadManager::RunOnStatusUpdateThread(const std::string& name, - std::function runnable) { +void PayloadManager::RunOnStatusUpdateThread( + const std::string& name, absl::AnyInvocable runnable) { payload_status_update_executor_.Execute(name, std::move(runnable)); } -/////////////////////////////// PendingPayloads /////////////////////////////// +/////////////////////////////// PendingPayloads +////////////////////////////////// void PayloadManager::PendingPayloads::StartTrackingPayload( Payload::Id payload_id, std::unique_ptr pending_payload) { MutexLock lock(&mutex_); // If the |payload_id| is being re-used, always prefer the newer payload. - auto it = pending_payloads_.find(payload_id); - if (it != pending_payloads_.end()) { - pending_payloads_.erase(payload_id); - } - auto pair = pending_payloads_.emplace(payload_id, std::move(pending_payload)); - NEARBY_LOGS(INFO) << "StartTrackingPayload: payload_id=" << payload_id - << "; inserted=" << pair.second; + Remove(pending_payloads_.find(payload_id)); + NEARBY_LOGS(INFO) << "StartTrackingPayload: " << pending_payload->ToString(); + pending_payload->IncRefCount(); + pending_payloads_[payload_id] = std::move(pending_payload); } -std::unique_ptr -PayloadManager::PendingPayloads::StopTrackingPayload(Payload::Id payload_id) { +void PayloadManager::PendingPayloads::StopTrackingPayload( + Payload::Id payload_id) { MutexLock lock(&mutex_); - - auto it = pending_payloads_.find(payload_id); - if (it == pending_payloads_.end()) return {}; - - auto item = pending_payloads_.extract(it); - return std::move(item.mapped()); + NEARBY_LOGS(INFO) << "StopTrackingPayload " << payload_id; + Remove(pending_payloads_.find(payload_id)); } -PayloadManager::PendingPayload* PayloadManager::PendingPayloads::GetPayload( - Payload::Id payload_id) const { +void PayloadManager::PendingPayloads::Remove( + absl::flat_hash_map>::iterator + it) { + if (it != pending_payloads_.end()) { + int refcount = it->second->DecRefCount(); + if (refcount == 0) { + // Nobody is using the payload, we can remove it. + NEARBY_LOGS(VERBOSE) << "Erase payload " << it->second->ToString(); + pending_payloads_.erase(it); + } else { + // Someone is still using the payload. Move it to the garbage bin. The + // payload will be removed when they release it. + NEARBY_LOGS(VERBOSE) << "Bin payload " << it->second->ToString(); + payload_garbage_bin_.push_back( + std::move(pending_payloads_.extract(it).mapped())); + } + } +} + +PayloadManager::PendingPayloadHandle +PayloadManager::PendingPayloads::GetPayload(Payload::Id payload_id) const { MutexLock lock(&mutex_); auto item = pending_payloads_.find(payload_id); - return item != pending_payloads_.end() ? item->second.get() : nullptr; + if (item == pending_payloads_.end()) { + return PendingPayloadHandle(); + } + PendingPayload* payload = item->second.get(); + payload->IncRefCount(); + return PendingPayloadHandle( + payload, absl::bind_front(&PendingPayloads::Release, + const_cast(this))); } -std::vector PayloadManager::PendingPayloads::GetAllPayloads() { +void PayloadManager::PendingPayloads::StopTrackingAllPayloads() { MutexLock lock(&mutex_); - std::vector result; - for (const auto& item : pending_payloads_) { - result.push_back(item.first); + for (auto it = pending_payloads_.begin(); it != pending_payloads_.end();) { + Remove(it++); } - return result; +} + +void PayloadManager::PendingPayloads::ForEachPayload( + absl::AnyInvocable callback) { + MutexLock lock(&mutex_); + + for (const auto& item : pending_payloads_) { + callback(item.second.get()); + } +} + +void PayloadManager::PendingPayloads::Release(PendingPayload* payload) { + // Called when `PendingPayloadHandle` is destroyed. + MutexLock lock(&mutex_); + NEARBY_LOGS(VERBOSE) << __func__ << " " << payload->ToString(); + auto it = pending_payloads_.find(payload->GetId()); + if (it != pending_payloads_.end() && it->second.get() == payload) { + // The payload is still tracked. + payload->DecRefCount(); + return; + } + auto bin_it = + std::find_if(payload_garbage_bin_.begin(), payload_garbage_bin_.end(), + [payload](auto& item) { return item.get() == payload; }); + if (bin_it != payload_garbage_bin_.end()) { + int refcount = payload->DecRefCount(); + if (refcount == 0) { + // The payload is not tracked and it was the last reference. + payload_garbage_bin_.erase(bin_it); + } + } +} + +PayloadManager::PendingPayloadHandle::PendingPayloadHandle( + PendingPayload* payload, DestroyCallback destroy_callback) + : payload_(payload), destroy_callback_(std::move(destroy_callback)) {} + +PayloadManager::PendingPayloadHandle::~PendingPayloadHandle() { + if (destroy_callback_) { + std::move(destroy_callback_)(payload_); + } +} + +std::string PayloadManager::PendingPayload::ToString() const { + return absl::StrFormat("Payload(%s, %d)", + IsIncoming() ? "incoming" : "outgoing", GetId()); } } // namespace connections diff --git a/connections/implementation/payload_manager.h b/connections/implementation/payload_manager.h index 6be21457..eeb46b93 100644 --- a/connections/implementation/payload_manager.h +++ b/connections/implementation/payload_manager.h @@ -15,6 +15,7 @@ #ifndef CORE_INTERNAL_PAYLOAD_MANAGER_H_ #define CORE_INTERNAL_PAYLOAD_MANAGER_H_ +#include #include #include #include @@ -23,6 +24,7 @@ #include #include "absl/container/flat_hash_map.h" +#include "absl/functional/any_invocable.h" #include "connections/implementation/analytics/packet_meta_data.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_manager.h" @@ -99,12 +101,19 @@ 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); + const EndpointIds& endpoint_ids, bool is_incoming, + DestroyCallback destroy_callback); PendingPayload(PendingPayload&&) = default; PendingPayload& operator=(PendingPayload&&) = default; - ~PendingPayload() { Close(); } + ~PendingPayload() { + Close(); + if (destroy_callback_) { + std::move(destroy_callback_)(this); + } + } Payload::Id GetId() const; @@ -137,24 +146,65 @@ class PayloadManager : public EndpointManager::FrameProcessor { void SetOffsetForEndpoint(const std::string& endpoint_id, std::int64_t offset) ABSL_LOCKS_EXCLUDED(mutex_); - // Closes internal_payload_ and triggers close_event_. + // Closes internal_payload_. // Close is called when a pending peyload does not have associated // endpoints. void Close(); - // Waits for close_event_ or for timeout to happen. - // Returns true, if event happened, false otherwise. - bool WaitForClose(); - bool IsClosed(); + std::string ToString() const; + + // Ref counting for `PendingPayloads` use only. `PendingPayloads` class owns + // all instances of `PendingPayload`. + int IncRefCount() { return ++refcount_; } + int DecRefCount() { return --refcount_; } private: mutable Mutex mutex_; bool is_incoming_; AtomicBoolean is_locally_canceled_{false}; - CountDownLatch close_event_{1}; + AtomicBoolean is_closed_; std::unique_ptr internal_payload_; + DestroyCallback destroy_callback_; absl::flat_hash_map endpoints_ ABSL_GUARDED_BY(mutex_); + int refcount_ = 0; + }; + + // A RAII handle to `PendingPayload`. Holding a `PendingPayloadHandle` + // guarantees that `PendingPaylaod` won't be destroyed while in use. + // Create instances with `GetPayload(Payload::Id)`. + class PendingPayloadHandle { + public: + using DestroyCallback = absl::AnyInvocable; + PendingPayloadHandle() = default; + PendingPayloadHandle(PendingPayload* payload, + DestroyCallback destroy_callback); + PendingPayloadHandle(const PendingPayloadHandle&) = delete; + PendingPayloadHandle(PendingPayloadHandle&& other) { + payload_ = other.payload_; + other.payload_ = nullptr; + destroy_callback_ = std::move(other.destroy_callback_); + } + ~PendingPayloadHandle(); + PendingPayloadHandle& operator=(const PendingPayloadHandle&) = delete; + PendingPayloadHandle& operator=(PendingPayloadHandle&& other) { + if (payload_ != nullptr && destroy_callback_) { + std::move(destroy_callback_)(payload_); + } + payload_ = other.payload_; + other.payload_ = nullptr; + destroy_callback_ = std::move(other.destroy_callback_); + return *this; + } + explicit operator bool() const { return payload_ != nullptr; } + + PendingPayload* operator->() const { return payload_; } + + PendingPayload& operator*() const { return *payload_; } + + private: + PendingPayload* payload_ = nullptr; + DestroyCallback destroy_callback_; }; // Tracks and manages PendingPayload objects in a synchronized manner. @@ -166,16 +216,30 @@ class PayloadManager : public EndpointManager::FrameProcessor { void StartTrackingPayload(Payload::Id payload_id, std::unique_ptr pending_payload) ABSL_LOCKS_EXCLUDED(mutex_); - std::unique_ptr StopTrackingPayload(Payload::Id payload_id) + void StopTrackingPayload(Payload::Id payload_id) ABSL_LOCKS_EXCLUDED(mutex_); - PendingPayload* GetPayload(Payload::Id payload_id) const + void StopTrackingAllPayloads() ABSL_LOCKS_EXCLUDED(mutex_); + PendingPayloadHandle GetPayload(Payload::Id payload_id) const + ABSL_LOCKS_EXCLUDED(mutex_); + // Calls `callback` for each tracked payload. The callback must not call + // other `PendingPayloads` methods. + void ForEachPayload(absl::AnyInvocable callback) ABSL_LOCKS_EXCLUDED(mutex_); - std::vector GetAllPayloads() ABSL_LOCKS_EXCLUDED(mutex_); private: + void Release(PendingPayload* payload) ABSL_LOCKS_EXCLUDED(mutex_); + void Remove(absl::flat_hash_map< + Payload::Id, std::unique_ptr>::iterator it) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); mutable Mutex mutex_; absl::flat_hash_map> pending_payloads_ ABSL_GUARDED_BY(mutex_); + // When we stop tracking a payload but someone is still holding a handle to + // the payload, we can't delete it just yet. Instead, we move it to the + // garbage bin. When the `PendingPayloadHandle` is released, the payload + // will be removed from the bin. + std::vector> payload_garbage_bin_ + ABSL_GUARDED_BY(mutex_); }; using Endpoints = std::vector; @@ -185,8 +249,8 @@ class PayloadManager : public EndpointManager::FrameProcessor { 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. + // 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); @@ -203,14 +267,14 @@ class PayloadManager : public EndpointManager::FrameProcessor { std::int64_t offset_bytes, location::nearby::proto::connections::PayloadStatus status); - // Converts the status of an endpoint that's been set out-of-band via a remote - // ControlMessage to the PayloadStatus for handling of that endpoint-payload - // pair. + // Converts the status of an endpoint that's been set out-of-band via a + // remote ControlMessage to the PayloadStatus for handling of that + // endpoint-payload pair. static location::nearby::proto::connections::PayloadStatus EndpointInfoStatusToPayloadStatus(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_*. + // 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( PayloadTransferFrame::ControlMessage::EventType event); @@ -226,8 +290,8 @@ class PayloadManager : public EndpointManager::FrameProcessor { PayloadTransferFrame::PayloadChunk CreatePayloadChunk(std::int64_t offset, ByteArray body); - PendingPayload* CreateIncomingPayload(const PayloadTransferFrame& frame, - const std::string& endpoint_id) + PendingPayloadHandle CreateIncomingPayload(const PayloadTransferFrame& frame, + const std::string& endpoint_id) ABSL_LOCKS_EXCLUDED(mutex_); Payload::Id CreateOutgoingPayload(Payload payload, @@ -251,8 +315,8 @@ class PayloadManager : public EndpointManager::FrameProcessor { std::int64_t num_bytes_successfully_transferred, PayloadTransferFrame::ControlMessage::EventType event_type); - // Handles a finished outgoing payload for the given endpointIds. All statuses - // except for SUCCESS are handled here. + // 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, const PayloadTransferFrame::PayloadHeader& payload_header, @@ -293,11 +357,11 @@ class PayloadManager : public EndpointManager::FrameProcessor { SingleThreadExecutor* GetOutgoingPayloadExecutor(PayloadType payload_type); void RunOnStatusUpdateThread(const std::string& name, - std::function runnable); + absl::AnyInvocable runnable); bool NotifyShutdown() ABSL_LOCKS_EXCLUDED(mutex_); void DestroyPendingPayload(Payload::Id payload_id) ABSL_LOCKS_EXCLUDED(mutex_); - PendingPayload* GetPayload(Payload::Id payload_id) const + PendingPayloadHandle GetPayload(Payload::Id payload_id) const ABSL_LOCKS_EXCLUDED(mutex_); void CancelAllPayloads() ABSL_LOCKS_EXCLUDED(mutex_); @@ -317,23 +381,23 @@ class PayloadManager : public EndpointManager::FrameProcessor { PayloadType FramePayloadTypeToPayloadType( PayloadTransferFrame::PayloadHeader::PayloadType type); + void OnPendingPayloadDestroy(const PendingPayload* payload); mutable Mutex mutex_; std::string custom_save_path_; AtomicBoolean shutdown_{false}; std::unique_ptr shutdown_barrier_; int send_payload_count_ = 0; - PendingPayloads pending_payloads_ ABSL_GUARDED_BY(mutex_); SingleThreadExecutor bytes_payload_executor_; SingleThreadExecutor file_payload_executor_; SingleThreadExecutor stream_payload_executor_; SingleThreadExecutor payload_status_update_executor_; - + PendingPayloads pending_payloads_; EndpointManager* endpoint_manager_; // When callback processing cannot keep the speed of callback update, the // callback thread will be lag to the real transfer. In order to keep sync - // between callback and sending/receiving threads, we will skip non-important - // callbacks during file transfer. + // between callback and sending/receiving threads, we will skip + // non-important callbacks during file transfer. mutable Mutex chunk_update_mutex_; int outgoing_chunk_update_count_ ABSL_GUARDED_BY(chunk_update_mutex_) = 0; int incoming_chunk_update_count_ ABSL_GUARDED_BY(chunk_update_mutex_) = 0; From dfdb5a6405d0ddd637a75332bfaa5711546c58b9 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Tue, 25 Jul 2023 15:08:48 -0700 Subject: [PATCH 007/128] Fix crash in BleV2MediumTest::StartScanningTmp This fixes flakiness in the tests. PiperOrigin-RevId: 551009093 --- internal/platform/ble_v2.cc | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/internal/platform/ble_v2.cc b/internal/platform/ble_v2.cc index 780d34c0..fbe765d4 100644 --- a/internal/platform/ble_v2.cc +++ b/internal/platform/ble_v2.cc @@ -89,7 +89,7 @@ bool BleV2Medium::StartScanning(const Uuid& service_uuid, MutexLock lock(&mutex_); if (!peripherals_.contains(&peripheral)) { NEARBY_LOGS(INFO) << "Peripheral impl=" << &peripheral - << " is not existed; adds it to the map."; + << " does not exist; add it to the map."; peripherals_.insert(&peripheral); } @@ -134,25 +134,24 @@ std::unique_ptr BleV2Medium::StartScanningTmp( const Uuid& service_uuid, api::ble_v2::TxPowerLevel tx_power_level, api::ble_v2::BleMedium::ScanningCallback callback) { - // auto scan_callback = std::move(callback.advertisement_found_cb); MutexLock lock(&mutex_); if (impl_->StartScanning( service_uuid, tx_power_level, api::ble_v2::BleMedium::ScanCallback{ .advertisement_found_cb = - [this, &callback](api::ble_v2::BlePeripheral& peripheral, - BleAdvertisementData advertisement_data) { + [this, + found_callback = std::move(callback.advertisement_found_cb)]( + api::ble_v2::BlePeripheral& peripheral, + BleAdvertisementData advertisement_data) mutable { MutexLock lock(&mutex_); if (!peripherals_.contains(&peripheral)) { NEARBY_LOGS(INFO) - << "There is no need to callback due to peripheral " - "impl=" - << &peripheral << ", which already exists."; + << "Peripheral impl=" << &peripheral + << " does not exist; add it to the map."; peripherals_.insert(&peripheral); } - callback.advertisement_found_cb(peripheral, - advertisement_data); + found_callback(peripheral, advertisement_data); }, })) { callback.start_scanning_result(absl::OkStatus()); From 2bdafe926ceaf6e8c54743f3a134a3d46b6f294f Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Tue, 25 Jul 2023 16:17:40 -0700 Subject: [PATCH 008/128] Add UX to show pairing result PiperOrigin-RevId: 551027791 --- fastpair/keyed_service/fast_pair_mediator.cc | 50 ++----- fastpair/keyed_service/fast_pair_mediator.h | 4 +- fastpair/ui/BUILD | 1 + fastpair/ui/actions.h | 3 +- ...st_pair_notification_controller_observer.h | 45 +++--- .../ui/fast_pair/fake_fast_pair_presenter.h | 11 ++ .../fast_pair_notification_controller.cc | 14 ++ .../fast_pair_notification_controller.h | 6 + .../fast_pair_notification_controller_test.cc | 75 +++++----- fastpair/ui/fast_pair/fast_pair_presenter.h | 6 +- .../ui/fast_pair/fast_pair_presenter_impl.cc | 7 + .../ui/fast_pair/fast_pair_presenter_impl.h | 4 + .../fast_pair_presenter_impl_test.cc | 128 ++++++++++-------- fastpair/ui/mock_ui_broker.h | 4 + fastpair/ui/ui_broker.h | 5 + fastpair/ui/ui_broker_impl.cc | 17 +++ fastpair/ui/ui_broker_impl.h | 4 + fastpair/ui/ui_broker_impl_test.cc | 9 +- 18 files changed, 239 insertions(+), 154 deletions(-) diff --git a/fastpair/keyed_service/fast_pair_mediator.cc b/fastpair/keyed_service/fast_pair_mediator.cc index 44913753..e6ddd041 100644 --- a/fastpair/keyed_service/fast_pair_mediator.cc +++ b/fastpair/keyed_service/fast_pair_mediator.cc @@ -21,14 +21,15 @@ #include #include "absl/status/status.h" +#include "fastpair/common/fast_pair_device.h" #include "fastpair/common/fast_pair_prefs.h" #include "fastpair/common/protocol.h" #include "fastpair/internal/mediums/mediums.h" #include "fastpair/pairing/pairer_broker_impl.h" #include "fastpair/repository/fast_pair_device_repository.h" +#include "fastpair/repository/fast_pair_repository_impl.h" #include "fastpair/scanning/scanner_broker_impl.h" #include "fastpair/server_access/fast_pair_client_impl.h" -#include "fastpair/repository/fast_pair_repository_impl.h" #include "fastpair/ui/actions.h" #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" #include "fastpair/ui/ui_broker_impl.h" @@ -98,21 +99,14 @@ void Mediator::OnDeviceFound(FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__ << ": Ignoring because show UI flag is false"; return; } - if (IsDeviceCurrentlyShowingNotification(device)) { - NEARBY_LOGS(VERBOSE) << __func__ - << ": Extending notification for re-discovered device=" - << *device_currently_showing_notification_; - // TODO(b/278768167): Add ui_broker_->ExtendNotification(); - return; - } else if (device_currently_showing_notification_) { + if (foreground_currently_showing_notification_) { NEARBY_LOGS(VERBOSE) << __func__ - << ": Already showing a notification for a different device= " - << *device_currently_showing_notification_; + << ": Already showing a notification for a different device= "; return; } // Show discovery notification - device_currently_showing_notification_ = &device; + foreground_currently_showing_notification_ = true; ui_broker_->ShowDiscovery(device, *notification_controller_); } @@ -125,8 +119,6 @@ void Mediator::OnDiscoveryAction(FastPairDevice& device, switch (action) { case DiscoveryAction::kPairToDevice: NEARBY_LOGS(INFO) << __func__ << ": Action = kPairToDevice"; - // TODO(285451051): Adding show pairing for higher than v1 version in ui - // broker pairer_broker_->PairDevice(device); break; case DiscoveryAction::kDismissedByOs: @@ -136,23 +128,29 @@ void Mediator::OnDiscoveryAction(FastPairDevice& device, // When the user explicitly dismisses the discovery notification, update // the device's block-list value accordingly. NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByUser"; - // TODO(285453663): update discovery block list + foreground_currently_showing_notification_ = false; + // TODO(b/285453663): update discovery block list [[fallthrough]]; case DiscoveryAction::kDismissedByTimeout: NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByTimeout"; - device_currently_showing_notification_ = nullptr; + foreground_currently_showing_notification_ = false; break; case DiscoveryAction::kLearnMore: NEARBY_LOGS(INFO) << __func__ << ": Action = kLearnMore"; break; + case DiscoveryAction::kDone: + NEARBY_LOGS(INFO) << __func__ << ": Action = kDone"; + foreground_currently_showing_notification_ = false; + break; default: - NEARBY_LOGS(INFO) << __func__ << ": Action = kUnknow"; + NEARBY_LOGS(INFO) << __func__ << ": Action = Unknown"; break; } } void Mediator::OnDevicePaired(FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__ << ": " << device; + ui_broker_->ShowPairingResult(device, *notification_controller_, true); } void Mediator::OnAccountKeyWrite(FastPairDevice& device, @@ -176,7 +174,7 @@ void Mediator::OnPairingComplete(FastPairDevice& device) { void Mediator::OnPairFailure(FastPairDevice& device, PairFailure failure) { NEARBY_LOGS(INFO) << __func__ << ": " << device << " with PairFailure: " << failure; - // TODO: UI showPairingFailed + ui_broker_->ShowPairingResult(device, *notification_controller_, false); } void Mediator::StartScanning() { @@ -212,24 +210,6 @@ bool Mediator::IsFastPairEnabled() { return true; } -bool Mediator::IsDeviceCurrentlyShowingNotification( - const FastPairDevice& device) { - // BLE addresses could have rotated, causing this check to return false for - // the same device. Fast Pair considers a device different if they have - // different BLE addresses. Similarly, the this check will fail if it is the - // same physical device under different scenarios: for example, if a device - // is found via the initial scenario and via the subsequent scenario, Fast - // Pair does not consider them the same device. - - return device_currently_showing_notification_ && - device_currently_showing_notification_->GetModelId() == - device.GetModelId() && - device_currently_showing_notification_->GetBleAddress() == - device.GetBleAddress() && - device_currently_showing_notification_->GetProtocol() != - device.GetProtocol(); -} - void Mediator::SetIsScreenLocked(bool locked) { executor_->Execute( "on_lock_state_changed", diff --git a/fastpair/keyed_service/fast_pair_mediator.h b/fastpair/keyed_service/fast_pair_mediator.h index 7b2fbeef..16c4f6a2 100644 --- a/fastpair/keyed_service/fast_pair_mediator.h +++ b/fastpair/keyed_service/fast_pair_mediator.h @@ -102,9 +102,7 @@ class Mediator final : public ScannerBroker::Observer, void InvalidateScanningState() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); bool IsDeviceCurrentlyShowingNotification(const FastPairDevice& device); - // |device_currently_showing_notification_| can be null if there is no - // notification currently displayed to the user. - FastPairDevice* device_currently_showing_notification_ = nullptr; + bool foreground_currently_showing_notification_ = false; FastPairHttpNotifier fast_pair_http_notifier_; std::unique_ptr executor_; std::unique_ptr mediums_; diff --git a/fastpair/ui/BUILD b/fastpair/ui/BUILD index 52fef3f8..16f74cbc 100644 --- a/fastpair/ui/BUILD +++ b/fastpair/ui/BUILD @@ -88,6 +88,7 @@ cc_test( ":fake_fast_pair_ui", ":fast_pair_ui", "//fastpair/common", + "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_googletest//:gtest_main", diff --git a/fastpair/ui/actions.h b/fastpair/ui/actions.h index 6228d4ee..a7910281 100644 --- a/fastpair/ui/actions.h +++ b/fastpair/ui/actions.h @@ -24,7 +24,8 @@ enum class DiscoveryAction { kDismissedByUser = 2, kDismissedByOs = 3, kLearnMore = 4, - kDismissedByTimeout = 5, + kDone = 5, + kDismissedByTimeout = 6, }; } // namespace fastpair diff --git a/fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h b/fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h index 2c1a15d9..e494fb8d 100644 --- a/fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h +++ b/fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h @@ -23,6 +23,7 @@ #include "fastpair/common/device_metadata.h" #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/mutex_lock.h" namespace nearby { namespace fastpair { @@ -30,35 +31,45 @@ class FakeFastPairNotificationControllerObserver : public FastPairNotificationController::Observer { public: explicit FakeFastPairNotificationControllerObserver( - std::optional latch) { - latch_ = latch; + CountDownLatch* on_device_updated_latch, + CountDownLatch* on_pairing_result_latch) { + on_device_updated_latch_ = on_device_updated_latch; + on_pairing_result_latch_ = on_pairing_result_latch; } void OnUpdateDevice(const DeviceMetadata& device) override { - device_metadata_name_list_.push_back(device.GetDetails().name()); - on_update_device_count_++; - if (latch_.has_value()) { - latch_->CountDown(); + MutexLock lock(&mutex_); + device_ = &const_cast(device); + if (on_device_updated_latch_) { + on_device_updated_latch_->CountDown(); } } - bool CheckDeviceMetadataListContainTestDevice( - const std::string& device_name) { - auto it = std::find(device_metadata_name_list_.begin(), - device_metadata_name_list_.end(), device_name); - return it != device_metadata_name_list_.end(); + void OnPairingResult(const DeviceMetadata& device, bool success) override { + MutexLock lock(&mutex_); + pairing_result_ = success; + device_ = &const_cast(device); + if (on_pairing_result_latch_) { + on_pairing_result_latch_->CountDown(); + } } - int on_update_device_count() { return on_update_device_count_; } + DeviceMetadata* GetDevice() { + MutexLock lock(&mutex_); + return device_; + } - std::vector device_metadata_name_list() { - return device_metadata_name_list_; + std::optional GetPairingResult() { + MutexLock lock(&mutex_); + return pairing_result_; } private: - std::vector device_metadata_name_list_; - int on_update_device_count_ = 0; - std::optional latch_; + Mutex mutex_; + CountDownLatch* on_device_updated_latch_; + CountDownLatch* on_pairing_result_latch_; + DeviceMetadata* device_ ABSL_GUARDED_BY(mutex_) = nullptr; + std::optional pairing_result_ ABSL_GUARDED_BY(mutex_); }; } // namespace fastpair } // namespace nearby diff --git a/fastpair/ui/fast_pair/fake_fast_pair_presenter.h b/fastpair/ui/fast_pair/fake_fast_pair_presenter.h index b5a1c89e..052fd955 100644 --- a/fastpair/ui/fast_pair/fake_fast_pair_presenter.h +++ b/fastpair/ui/fast_pair/fake_fast_pair_presenter.h @@ -35,13 +35,24 @@ class FakeFastPairPresenter : public FastPairPresenter { version_changed_ = true; callback(DiscoveryAction::kPairToDevice); } + + void ShowPairingResult( + FastPairDevice& device, + FastPairNotificationController& notification_controller, + bool success) override { + pairing_result_changed_ = true; + } + bool show_discovery() { return show_discovery_; } bool version_changed() { return version_changed_; } + bool pairing_result_changed() { return pairing_result_changed_; } + private: bool show_discovery_ = false; bool version_changed_ = false; + bool pairing_result_changed_ = false; }; class FakeFastPairPresenterFactory : public FastPairPresenterImpl::Factory { diff --git a/fastpair/ui/fast_pair/fast_pair_notification_controller.cc b/fastpair/ui/fast_pair/fast_pair_notification_controller.cc index e38e4405..f19396dd 100644 --- a/fastpair/ui/fast_pair/fast_pair_notification_controller.cc +++ b/fastpair/ui/fast_pair/fast_pair_notification_controller.cc @@ -39,6 +39,14 @@ void FastPairNotificationController::NotifyShowDiscovery( } } +void FastPairNotificationController::NotifyShowPairingResult( + const DeviceMetadata& device, bool success) { + NEARBY_LOGS(INFO) << __func__; + for (Observer* observer : observers_.GetObservers()) { + observer->OnPairingResult(device, success); + } +} + void FastPairNotificationController::ShowGuestDiscoveryNotification( const DeviceMetadata& device, DiscoveryCallback callback) { callback_ = std::move(callback); @@ -46,6 +54,12 @@ void FastPairNotificationController::ShowGuestDiscoveryNotification( NotifyShowDiscovery(device); } +void FastPairNotificationController::ShowPairingResultNotification( + const DeviceMetadata& device, bool success) { + NEARBY_LOGS(INFO) << __func__ << "Notify show pairing result notification. "; + NotifyShowPairingResult(device, success); +} + void FastPairNotificationController::OnDiscoveryClicked( DiscoveryAction action) { NEARBY_LOGS(INFO) << __func__ diff --git a/fastpair/ui/fast_pair/fast_pair_notification_controller.h b/fastpair/ui/fast_pair/fast_pair_notification_controller.h index 9f792225..7978ee4d 100644 --- a/fastpair/ui/fast_pair/fast_pair_notification_controller.h +++ b/fastpair/ui/fast_pair/fast_pair_notification_controller.h @@ -39,6 +39,8 @@ class FastPairNotificationController { public: virtual ~Observer() = default; virtual void OnUpdateDevice(const DeviceMetadata& device) = 0; + virtual void OnPairingResult(const DeviceMetadata& device, + bool success) = 0; }; FastPairNotificationController() = default; @@ -52,11 +54,15 @@ class FastPairNotificationController { void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); void NotifyShowDiscovery(const DeviceMetadata& device); + void NotifyShowPairingResult(const DeviceMetadata& device, bool success); // Creates and displays corresponding notification. void ShowGuestDiscoveryNotification(const DeviceMetadata& device_metadata, DiscoveryCallback callback); + void ShowPairingResultNotification(const DeviceMetadata& device_metadata, + bool success); + // Triggers callback when the related action is clicked. void OnDiscoveryClicked(DiscoveryAction action); diff --git a/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc b/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc index 871dd4a5..081f6f79 100644 --- a/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc +++ b/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc @@ -25,6 +25,7 @@ #include "fastpair/common/device_metadata.h" #include "fastpair/ui/actions.h" #include "fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h" +#include "internal/platform/count_down_latch.h" namespace nearby { namespace fastpair { @@ -34,46 +35,46 @@ const int64_t kDeviceId = 10148625; const char kModelId[] = "9adb11"; const char kDeviceName[] = "Pixel Buds Pro"; -class FastPairNotificationControllerTest : public ::testing::Test { - protected: - FastPairNotificationControllerTest() { - notification_controller_obsesrver_ = - std::make_unique( - std::nullopt); - notification_controller_.AddObserver( - notification_controller_obsesrver_.get()); - } - - void TriggerOnUpdateDevice(DeviceMetadata& device, - DiscoveryCallback callback) { - notification_controller_.ShowGuestDiscoveryNotification( - device, std::move(callback)); - } - - void DiscoveryActionClicked(DiscoveryAction action) { - discovery_action_ = action; - } - - FastPairNotificationController notification_controller_; - std::unique_ptr - notification_controller_obsesrver_; - DiscoveryAction discovery_action_; -}; - -TEST_F(FastPairNotificationControllerTest, ShowGuestDiscoveryNotification) { +TEST(FastPairNotificationControllerTest, ShowGuestDiscoveryNotification) { + FastPairNotificationController notification_controller; proto::GetObservedDeviceResponse response; - response.mutable_device()->set_id(kDeviceId); - response.mutable_device()->set_name(kDeviceName); DeviceMetadata device_metadata(response); - TriggerOnUpdateDevice(device_metadata, [this](DiscoveryAction action) { - DiscoveryActionClicked(action); - }); - EXPECT_TRUE(notification_controller_obsesrver_ - ->CheckDeviceMetadataListContainTestDevice(kDeviceName)); - EXPECT_EQ(1, notification_controller_obsesrver_->on_update_device_count()); - notification_controller_.OnDiscoveryClicked(DiscoveryAction::kPairToDevice); - EXPECT_EQ(DiscoveryAction::kPairToDevice, discovery_action_); + CountDownLatch on_update_device_latch(1); + CountDownLatch on_click_latch(1); + FakeFastPairNotificationControllerObserver observer(&on_update_device_latch, + nullptr); + notification_controller.AddObserver(&observer); + EXPECT_EQ(observer.GetDevice(), nullptr); + DiscoveryAction discovery_action = DiscoveryAction::kUnknown; + notification_controller.ShowGuestDiscoveryNotification( + device_metadata, [&](DiscoveryAction action) { + on_click_latch.CountDown(); + discovery_action = action; + }); + on_update_device_latch.Await(); + EXPECT_EQ(observer.GetDevice(), &device_metadata); + notification_controller.OnDiscoveryClicked(DiscoveryAction::kPairToDevice); + on_click_latch.Await(); + EXPECT_EQ(discovery_action, DiscoveryAction::kPairToDevice); +} + +TEST(FastPairNotificationControllerTest, ShowPairingResultNotification) { + FastPairNotificationController notification_controller; + proto::GetObservedDeviceResponse response; + DeviceMetadata device_metadata(response); + + CountDownLatch on_pairing_result_latch(1); + FakeFastPairNotificationControllerObserver observer(nullptr, + &on_pairing_result_latch); + notification_controller.AddObserver(&observer); + EXPECT_FALSE(observer.GetPairingResult().has_value()); + EXPECT_EQ(observer.GetDevice(), nullptr); + notification_controller.ShowPairingResultNotification(device_metadata, true); + on_pairing_result_latch.Await(); + EXPECT_EQ(observer.GetDevice(), &device_metadata); + EXPECT_TRUE(observer.GetPairingResult().has_value()); + EXPECT_TRUE(observer.GetPairingResult().value()); } } // namespace diff --git a/fastpair/ui/fast_pair/fast_pair_presenter.h b/fastpair/ui/fast_pair/fast_pair_presenter.h index 6bd980b4..50da908a 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter.h +++ b/fastpair/ui/fast_pair/fast_pair_presenter.h @@ -24,11 +24,15 @@ namespace fastpair { // This Presenter creates and manages UI component with Notification Controller. class FastPairPresenter { public: - // observer_list of notification_controller is updated virtual void ShowDiscovery( FastPairDevice& device, FastPairNotificationController& notification_controller, DiscoveryCallback callback) = 0; + virtual void ShowPairingResult( + FastPairDevice& device, + FastPairNotificationController& notification_controller, + bool success) = 0; + virtual ~FastPairPresenter() = default; }; diff --git a/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc b/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc index d242096c..6b9ff70c 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc +++ b/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc @@ -54,5 +54,12 @@ void FastPairPresenterImpl::ShowDiscovery( notification_controller.ShowGuestDiscoveryNotification(*device.GetMetadata(), std::move(callback)); } + +void FastPairPresenterImpl::ShowPairingResult( + FastPairDevice& device, + FastPairNotificationController& notification_controller, bool success) { + notification_controller.ShowPairingResultNotification(*device.GetMetadata(), + success); +} } // namespace fastpair } // namespace nearby diff --git a/fastpair/ui/fast_pair/fast_pair_presenter_impl.h b/fastpair/ui/fast_pair/fast_pair_presenter_impl.h index cc6595d2..f606658c 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter_impl.h +++ b/fastpair/ui/fast_pair/fast_pair_presenter_impl.h @@ -48,6 +48,10 @@ class FastPairPresenterImpl : public FastPairPresenter { void ShowDiscovery(FastPairDevice& device, FastPairNotificationController& notification_controller, DiscoveryCallback callback) override; + void ShowPairingResult( + FastPairDevice& device, + FastPairNotificationController& notification_controller, + bool success) override; }; } // namespace fastpair } // namespace nearby diff --git a/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc b/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc index cb699f4f..221004e5 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc +++ b/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc @@ -14,8 +14,8 @@ #include "fastpair/ui/fast_pair/fast_pair_presenter_impl.h" -#include - +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "fastpair/common/fast_pair_device.h" #include "fastpair/proto/fastpair_rpcs.proto.h" @@ -26,78 +26,88 @@ namespace nearby { namespace fastpair { +const int64_t kDeviceId = 10148625; constexpr absl::string_view kModelId = "000000"; constexpr absl::string_view kAddress = "00:00:00:00:00:00"; constexpr absl::string_view kPublicKey = "test public key"; +constexpr absl::string_view kInitialPairingdescription = + "InitialPairingdescription"; namespace { -TEST(FastPairPresenterImplTest, ShowDiscoveryForV1Version) { - // Setup repository with v1 version device metadata - FastPairDevice device(kModelId, kAddress, Protocol::kFastPairInitialPairing); - proto::GetObservedDeviceResponse response; - DeviceMetadata device_metadata(response); - device.SetMetadata(device_metadata); - - // Register FastPairNotificationControllerObserver - auto latch_1 = std::make_optional(1); - FastPairNotificationController controller; - FakeFastPairNotificationControllerObserver notification_controller_observer( - latch_1); - controller.AddObserver(¬ification_controller_observer); - EXPECT_EQ(notification_controller_observer.on_update_device_count(), 0); - - // FastPairPresenter ShowDiscovery - CountDownLatch latch_2(1); - FastPairPresenterImpl fast_pair_presenter; - DiscoveryAction discovery_action = DiscoveryAction::kUnknown; - fast_pair_presenter.ShowDiscovery(device, controller, - [&](DiscoveryAction action) { - discovery_action = action; - latch_2.CountDown(); - }); - latch_1->Await(); - EXPECT_EQ(notification_controller_observer.on_update_device_count(), 1); - EXPECT_EQ(device.GetVersion(), DeviceFastPairVersion::kV1); - controller.OnDiscoveryClicked(DiscoveryAction::kPairToDevice); - latch_2.Await(); - EXPECT_EQ(discovery_action, DiscoveryAction::kPairToDevice); +// A gMock matcher to match proto values. Use this matcher like: +// request/response proto, expected_proto; +// EXPECT_THAT(proto, MatchesProto(expected_proto)); +MATCHER_P( + MatchesProto, expected_proto, + absl::StrCat(negation ? "does not match" : "matches", + testing::PrintToString(expected_proto.SerializeAsString()))) { + return arg.SerializeAsString() == expected_proto.SerializeAsString(); } -TEST(FastPairPresenterImplTest, ShowDiscoveryForHigherThanV1Version) { - // Setup repository with HigherThanV1Version device metadata - FastPairDevice device(kModelId, kAddress, Protocol::kFastPairInitialPairing); - proto::GetObservedDeviceResponse response; - auto* higher_than_v1_version_device = response.mutable_device(); - higher_than_v1_version_device->mutable_anti_spoofing_key_pair() - ->set_public_key(kPublicKey); - DeviceMetadata device_metadata(response); - device.SetMetadata(device_metadata); +TEST(FastPairPresenterImplTest, ShowDiscovery) { + // Sets up proto::GetObservedDeviceResponse + proto::GetObservedDeviceResponse response_proto; + auto* device = response_proto.mutable_device(); + device->set_id(kDeviceId); + auto* observed_device_strings = response_proto.mutable_strings(); + observed_device_strings->set_initial_pairing_description( + kInitialPairingdescription); + DeviceMetadata device_metadata(response_proto); + FastPairDevice fast_pair_device(kModelId, kAddress, + Protocol::kFastPairInitialPairing); - // Register FastPairNotificationControllerObserver - auto latch_1 = std::make_optional(1); - FastPairNotificationController controller; - FakeFastPairNotificationControllerObserver notification_controller_observer( - latch_1); - controller.AddObserver(¬ification_controller_observer); - EXPECT_EQ(notification_controller_observer.on_update_device_count(), 0); + fast_pair_device.SetMetadata(device_metadata); - // FastPairPresenter ShowDiscovery - CountDownLatch latch_2(1); - FastPairPresenterImpl fast_pair_presenter; + FastPairNotificationController notification_controller; + CountDownLatch on_update_device_latch(1); + CountDownLatch on_click_latch(1); + FakeFastPairNotificationControllerObserver observer(&on_update_device_latch, + nullptr); + notification_controller.AddObserver(&observer); DiscoveryAction discovery_action = DiscoveryAction::kUnknown; - fast_pair_presenter.ShowDiscovery(device, controller, + FastPairPresenterImpl fast_pair_presenter; + fast_pair_presenter.ShowDiscovery(fast_pair_device, notification_controller, [&](DiscoveryAction action) { + on_click_latch.CountDown(); discovery_action = action; - latch_2.CountDown(); }); - latch_1->Await(); - EXPECT_EQ(notification_controller_observer.on_update_device_count(), 1); - EXPECT_EQ(device.GetVersion(), DeviceFastPairVersion::kHigherThanV1); - controller.OnDiscoveryClicked(DiscoveryAction::kDismissedByUser); - latch_2.Await(); - EXPECT_EQ(discovery_action, DiscoveryAction::kDismissedByUser); + on_update_device_latch.Await(); + EXPECT_EQ(observer.GetDevice()->GetFastPairVersion(), + DeviceFastPairVersion::kV1); + EXPECT_THAT(observer.GetDevice()->GetResponse(), + MatchesProto(response_proto)); } +TEST(FastPairPresenterImplTest, ShowPairingResult) { + // Sets up proto::GetObservedDeviceResponse + proto::GetObservedDeviceResponse response_proto; + auto* device = response_proto.mutable_device(); + device->set_id(kDeviceId); + auto* observed_device_strings = response_proto.mutable_strings(); + observed_device_strings->set_initial_pairing_description( + kInitialPairingdescription); + DeviceMetadata device_metadata(response_proto); + FastPairDevice fast_pair_device(kModelId, kAddress, + Protocol::kFastPairInitialPairing); + + fast_pair_device.SetMetadata(device_metadata); + + FastPairNotificationController notification_controller; + CountDownLatch on_pairing_result_latch(1); + FakeFastPairNotificationControllerObserver observer(nullptr, + &on_pairing_result_latch); + notification_controller.AddObserver(&observer); + FastPairPresenterImpl fast_pair_presenter; + fast_pair_presenter.ShowPairingResult(fast_pair_device, + notification_controller, true); + on_pairing_result_latch.Await(); + EXPECT_EQ(observer.GetDevice()->GetFastPairVersion(), + DeviceFastPairVersion::kV1); + EXPECT_THAT(observer.GetDevice()->GetResponse(), + MatchesProto(response_proto)); + EXPECT_TRUE(observer.GetPairingResult().has_value()); + EXPECT_TRUE(observer.GetPairingResult().value()); +} } // namespace } // namespace fastpair } // namespace nearby diff --git a/fastpair/ui/mock_ui_broker.h b/fastpair/ui/mock_ui_broker.h index f0467eae..70241c68 100644 --- a/fastpair/ui/mock_ui_broker.h +++ b/fastpair/ui/mock_ui_broker.h @@ -29,6 +29,10 @@ class MockUIBroker : public UIBroker { MOCK_METHOD(void, ShowDiscovery, (FastPairDevice&, FastPairNotificationController&), (override)); + MOCK_METHOD(void, ShowPairingResult, + (FastPairDevice&, FastPairNotificationController&, bool), + (override)); + void AddObserver(Observer* observer) override { observers_.AddObserver(observer); } diff --git a/fastpair/ui/ui_broker.h b/fastpair/ui/ui_broker.h index 6a414234..a6cfe1f2 100644 --- a/fastpair/ui/ui_broker.h +++ b/fastpair/ui/ui_broker.h @@ -43,6 +43,11 @@ class UIBroker { virtual void ShowDiscovery( FastPairDevice& device, FastPairNotificationController& notification_controller) = 0; + + virtual void ShowPairingResult( + FastPairDevice& device, + FastPairNotificationController& notification_controller, + bool success) = 0; }; } // namespace fastpair diff --git a/fastpair/ui/ui_broker_impl.cc b/fastpair/ui/ui_broker_impl.cc index 027e5db4..6b3c7b3c 100644 --- a/fastpair/ui/ui_broker_impl.cc +++ b/fastpair/ui/ui_broker_impl.cc @@ -60,6 +60,23 @@ void UIBrokerImpl::ShowDiscovery( } } +void UIBrokerImpl::ShowPairingResult( + FastPairDevice& device, + FastPairNotificationController& notification_controller, bool success) { + NEARBY_LOGS(VERBOSE) << __func__; + switch (device.GetProtocol()) { + case Protocol::kFastPairInitialPairing: + case Protocol::kFastPairSubsequentPairing: + fast_pair_presenter_->ShowPairingResult(device, notification_controller, + success); + break; + case Protocol::kFastPairRetroactivePairing: + // In this scenario, we don't show the error UI because it would be + // misleading, since a pair failure is a retroactive pair failure. + break; + } +} + void UIBrokerImpl::NotifyDiscoveryAction(FastPairDevice& device, DiscoveryAction action) { for (auto& observer : observers_.GetObservers()) diff --git a/fastpair/ui/ui_broker_impl.h b/fastpair/ui/ui_broker_impl.h index 3dae1743..039b448b 100644 --- a/fastpair/ui/ui_broker_impl.h +++ b/fastpair/ui/ui_broker_impl.h @@ -37,6 +37,10 @@ class UIBrokerImpl : public UIBroker { void ShowDiscovery( FastPairDevice &device, FastPairNotificationController ¬ification_controller) override; + void ShowPairingResult( + FastPairDevice &device, + FastPairNotificationController ¬ification_controller, + bool success) override; private: void NotifyDiscoveryAction(FastPairDevice &device, DiscoveryAction action); diff --git a/fastpair/ui/ui_broker_impl_test.cc b/fastpair/ui/ui_broker_impl_test.cc index 70600c86..8fe29107 100644 --- a/fastpair/ui/ui_broker_impl_test.cc +++ b/fastpair/ui/ui_broker_impl_test.cc @@ -31,7 +31,7 @@ constexpr absl::string_view kAddress = "74:74:46:01:6C:21"; class UIBrokerImplTest : public ::testing::Test, public UIBroker::Observer { protected: - UIBrokerImplTest() { + void SetUp() override { presenter_factory_ = std::make_unique(); FastPairPresenterImpl::Factory::SetFactoryForTesting( presenter_factory_.get()); @@ -68,6 +68,13 @@ TEST_F(UIBrokerImplTest, ShowDiscoveryWithoutObserver) { EXPECT_FALSE(on_discovery_action_notified_); } +TEST_F(UIBrokerImplTest, ShowPairingResult) { + FastPairDevice device(kModelId, kAddress, Protocol::kFastPairInitialPairing); + ui_broker_->ShowPairingResult(device, notification_controller_, true); + EXPECT_TRUE( + presenter_factory_->fake_fast_pair_presenter()->pairing_result_changed()); +} + } // namespace } // namespace fastpair } // namespace nearby From 3456c419c87789c78ede0fb8b3f3f6ce49d8214a Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Tue, 25 Jul 2023 16:38:02 -0700 Subject: [PATCH 009/128] Check account log in status before attempt to write account key PiperOrigin-RevId: 551032914 --- fastpair/fast_pair_service.cc | 2 +- fastpair/internal/BUILD | 4 + fastpair/internal/fast_pair_seeker_impl.cc | 9 +- fastpair/internal/fast_pair_seeker_impl.h | 2 + .../internal/fast_pair_seeker_impl_test.cc | 43 +++++++-- fastpair/keyed_service/fast_pair_mediator.cc | 4 +- fastpair/pairing/BUILD | 5 + fastpair/pairing/fastpair/BUILD | 5 + .../pairing/fastpair/fast_pair_pairer_impl.cc | 19 +++- .../pairing/fastpair/fast_pair_pairer_impl.h | 7 +- .../fastpair/fast_pair_pairer_impl_test.cc | 94 ++++++++++++++++--- fastpair/pairing/pairer_broker_impl.cc | 10 +- fastpair/pairing/pairer_broker_impl.h | 5 +- fastpair/pairing/pairer_broker_impl_test.cc | 89 ++++++++++++++++-- 14 files changed, 251 insertions(+), 47 deletions(-) diff --git a/fastpair/fast_pair_service.cc b/fastpair/fast_pair_service.cc index f57eabd5..791821b3 100644 --- a/fastpair/fast_pair_service.cc +++ b/fastpair/fast_pair_service.cc @@ -106,7 +106,7 @@ FastPairService::FastPairService( [this](const FastPairDevice& device, RingEvent event) { OnRingEvent(device, std::move(event)); }}, - &executor_, &devices_); + &executor_, account_manager_.get(), &devices_); } FastPairService::~FastPairService() { executor_.Shutdown(); } diff --git a/fastpair/internal/BUILD b/fastpair/internal/BUILD index 45fbbb91..4ffade7c 100644 --- a/fastpair/internal/BUILD +++ b/fastpair/internal/BUILD @@ -19,6 +19,7 @@ cc_library( "//fastpair/repository:device_repository", "//fastpair/retroactive", "//fastpair/scanning:scanner", + "//internal/account", "//internal/platform:types", "@com_google_absl//absl/status", "@com_google_absl//absl/strings:str_format", @@ -34,12 +35,15 @@ cc_test( deps = [ ":internal", "//fastpair:fast_pair_events", + "//fastpair/common", "//fastpair/message_stream:fake_gatt_callbacks", "//fastpair/message_stream:fake_provider", "//fastpair/repository:test_support", + "//internal/account:test_support", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep + "//internal/test/google3_only:test", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/time", "@com_google_googletest//:gtest_main", diff --git a/fastpair/internal/fast_pair_seeker_impl.cc b/fastpair/internal/fast_pair_seeker_impl.cc index c59ac65d..6bb0ef3b 100644 --- a/fastpair/internal/fast_pair_seeker_impl.cc +++ b/fastpair/internal/fast_pair_seeker_impl.cc @@ -38,9 +38,14 @@ constexpr absl::Duration kCleanupTimeout = absl::Seconds(3); FastPairSeekerImpl::FastPairSeekerImpl(ServiceCallbacks callbacks, SingleThreadExecutor* executor, + AccountManager* account_manager, FastPairDeviceRepository* devices) - : callbacks_(std::move(callbacks)), executor_(executor), devices_(devices) { - pairer_broker_ = std::make_unique(mediums_, executor_); + : callbacks_(std::move(callbacks)), + executor_(executor), + account_manager_(account_manager), + devices_(devices) { + pairer_broker_ = + std::make_unique(mediums_, executor_, account_manager_); pairer_broker_->AddObserver(this); mediums_.GetBluetoothClassic().AddObserver(this); retro_detector_ = std::make_unique( diff --git a/fastpair/internal/fast_pair_seeker_impl.h b/fastpair/internal/fast_pair_seeker_impl.h index 968828ed..a281c545 100644 --- a/fastpair/internal/fast_pair_seeker_impl.h +++ b/fastpair/internal/fast_pair_seeker_impl.h @@ -63,6 +63,7 @@ class FastPairSeekerImpl : public FastPairSeekerExt, }; FastPairSeekerImpl(ServiceCallbacks callbacks, SingleThreadExecutor* executor, + AccountManager* account_manager, FastPairDeviceRepository* devices); ~FastPairSeekerImpl() override; @@ -117,6 +118,7 @@ class FastPairSeekerImpl : public FastPairSeekerExt, ServiceCallbacks callbacks_; SingleThreadExecutor* executor_; + AccountManager* account_manager_; FastPairDeviceRepository* devices_; Mediums mediums_; std::unique_ptr scanner_; diff --git a/fastpair/internal/fast_pair_seeker_impl_test.cc b/fastpair/internal/fast_pair_seeker_impl_test.cc index 2d015969..a48ba0fd 100644 --- a/fastpair/internal/fast_pair_seeker_impl_test.cc +++ b/fastpair/internal/fast_pair_seeker_impl_test.cc @@ -24,13 +24,17 @@ #include "gtest/gtest.h" #include "absl/time/clock.h" #include "absl/time/time.h" +#include "fastpair/common/fast_pair_prefs.h" #include "fastpair/fast_pair_events.h" #include "fastpair/message_stream/fake_gatt_callbacks.h" #include "fastpair/message_stream/fake_provider.h" #include "fastpair/repository/fake_fast_pair_repository.h" +#include "internal/account/fake_account_manager.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" #include "internal/platform/single_thread_executor.h" +#include "internal/platform/task_runner_impl.h" +#include "internal/test/google3_only/fake_authentication_manager.h" namespace nearby { namespace fastpair { @@ -46,8 +50,9 @@ constexpr absl::string_view kBobPublicKey = "F7D496A62ECA416351540AA343BC690A6109F551500666B83B1251FB84FA2860795EBD63D3" "B8836F44A9A3E28BB34017E015F5979305D849FDF8DE10123B61D2"; constexpr absl::string_view kPasskey = "123456"; - -constexpr absl::Duration kTaskWaitTimeout = absl::Milliseconds(100); +constexpr absl::string_view kFastPairPreferencesFilePath = + "Google/Nearby/FastPair"; +constexpr absl::string_view kTestAccountId = "test_account_id"; using ::testing::status::StatusIs; @@ -59,16 +64,33 @@ class MediumEnvironmentStarter { class FastPairSeekerImplTest : public testing::Test { protected: + FastPairSeekerImplTest() { + task_runner_ = std::make_unique(1); + preferences_manager_ = std::make_unique( + kFastPairPreferencesFilePath); + authentication_manager_ = std::make_unique(); + } + void SetUp() override { NEARBY_LOG_SET_SEVERITY(VERBOSE); repository_ = FakeFastPairRepository::Create( kModelId, absl::HexStringToBytes(kBobPublicKey)); + account_manager_ = std::make_unique( + preferences_manager_.get(), prefs::kNearbyFastPairUsersName, + authentication_manager_.get(), task_runner_.get()); + AccountManager::Account account; + account.id = kTestAccountId; + account_manager_->SetAccount(account); } void TearDown() override { executor_.Shutdown(); } MediumEnvironmentStarter env_; SingleThreadExecutor executor_; + std::unique_ptr preferences_manager_; + std::unique_ptr authentication_manager_; + std::unique_ptr task_runner_; + std::unique_ptr account_manager_; FastPairDeviceRepository devices_{&executor_}; std::unique_ptr repository_; std::unique_ptr fast_pair_seeker_; @@ -77,7 +99,8 @@ class FastPairSeekerImplTest : public testing::Test { TEST_F(FastPairSeekerImplTest, StartAndStopFastPairScan) { fast_pair_seeker_ = std::make_unique( - FastPairSeekerImpl::ServiceCallbacks{}, &executor_, &devices_); + FastPairSeekerImpl::ServiceCallbacks{}, &executor_, + account_manager_.get(), &devices_); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); EXPECT_OK(fast_pair_seeker_->StopFastPairScan()); @@ -93,7 +116,7 @@ TEST_F(FastPairSeekerImplTest, DiscoverDevice) { EXPECT_EQ(device.GetModelId(), kModelId); latch.CountDown(); }}, - &executor_, &devices_); + &executor_, account_manager_.get(), &devices_); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); provider.StartDiscoverableAdvertisement(kModelId); @@ -104,7 +127,8 @@ TEST_F(FastPairSeekerImplTest, DiscoverDevice) { TEST_F(FastPairSeekerImplTest, StartFastPairScanTwiceFails) { fast_pair_seeker_ = std::make_unique( - FastPairSeekerImpl::ServiceCallbacks{}, &executor_, &devices_); + FastPairSeekerImpl::ServiceCallbacks{}, &executor_, + account_manager_.get(), &devices_); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); EXPECT_THAT(fast_pair_seeker_->StartFastPairScan(), @@ -113,7 +137,8 @@ TEST_F(FastPairSeekerImplTest, StartFastPairScanTwiceFails) { TEST_F(FastPairSeekerImplTest, StopFastPairScanTwiceFails) { fast_pair_seeker_ = std::make_unique( - FastPairSeekerImpl::ServiceCallbacks{}, &executor_, &devices_); + FastPairSeekerImpl::ServiceCallbacks{}, &executor_, + account_manager_.get(), &devices_); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); EXPECT_OK(fast_pair_seeker_->StopFastPairScan()); @@ -134,7 +159,7 @@ TEST_F(FastPairSeekerImplTest, ScreenLocksDuringAdvertising) { EXPECT_TRUE(event.is_locked); latch.CountDown(); }}, - &executor_, &devices_); + &executor_, account_manager_.get(), &devices_); // Create Advertiser and startAdvertising Mediums mediums_2; std::string service_id(kServiceID); @@ -170,7 +195,7 @@ TEST_F(FastPairSeekerImplTest, InitialPairing) { }})); discover_latch.CountDown(); }}, - &executor_, &devices_); + &executor_, account_manager_.get(), &devices_); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); provider.PrepareForInitialPairing( @@ -209,7 +234,7 @@ TEST_F(FastPairSeekerImplTest, RetroactivePairing) { retro_latch.CountDown(); }})); }}, - &executor_, &devices_); + &executor_, account_manager_.get(), &devices_); provider.PrepareForRetroactivePairing( {.private_key = absl::HexStringToBytes(kBobPrivateKey), diff --git a/fastpair/keyed_service/fast_pair_mediator.cc b/fastpair/keyed_service/fast_pair_mediator.cc index e6ddd041..a58c6986 100644 --- a/fastpair/keyed_service/fast_pair_mediator.cc +++ b/fastpair/keyed_service/fast_pair_mediator.cc @@ -75,8 +75,6 @@ Mediator::Mediator( devices_ = std::make_unique(executor_.get()); scanner_broker_ = std::make_unique( *mediums_, executor_.get(), devices_.get()); - pairer_broker_ = - std::make_unique(*mediums_, executor_.get()); task_runner_ = std::make_unique(1); preferences_manager_ = std::make_unique( kFastPairPreferencesFilePath); @@ -88,6 +86,8 @@ Mediator::Mediator( &fast_pair_http_notifier_, device_info_.get()); fast_pair_repository_ = std::make_unique(fast_pair_client_.get()); + pairer_broker_ = std::make_unique( + *mediums_, executor_.get(), account_manager_.get()); scanner_broker_->AddObserver(this); ui_broker_->AddObserver(this); pairer_broker_->AddObserver(this); diff --git a/fastpair/pairing/BUILD b/fastpair/pairing/BUILD index 44cb524c..65a485aa 100644 --- a/fastpair/pairing/BUILD +++ b/fastpair/pairing/BUILD @@ -33,6 +33,7 @@ cc_library( "//fastpair/handshake", "//fastpair/internal/mediums", "//fastpair/pairing/fastpair:pairing", + "//internal/account", "//internal/base", "//internal/platform:types", "@com_google_absl//absl/container:flat_hash_map", @@ -58,11 +59,15 @@ cc_test( "//fastpair/pairing/fastpair:pairing", "//fastpair/proto:fastpair_cc_proto", "//fastpair/repository:test_support", + "//internal/account", + "//internal/account:test_support", + "//internal/auth:credential", "//internal/base:bluetooth_address", "//internal/platform:comm", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep + "//internal/test/google3_only:test", "@boringssl//:crypto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/functional:bind_front", diff --git a/fastpair/pairing/fastpair/BUILD b/fastpair/pairing/fastpair/BUILD index eb6a212b..c748b211 100644 --- a/fastpair/pairing/fastpair/BUILD +++ b/fastpair/pairing/fastpair/BUILD @@ -34,6 +34,7 @@ cc_library( "//fastpair/handshake", "//fastpair/internal/mediums", "//fastpair/repository", + "//internal/account", "//internal/platform:comm", "//internal/platform:types", "@com_google_absl//absl/functional:any_invocable", @@ -56,11 +57,15 @@ cc_test( "//fastpair/handshake:test_support", "//fastpair/proto:fastpair_cc_proto", "//fastpair/repository:test_support", + "//internal/account", + "//internal/account:test_support", + "//internal/auth:credential", "//internal/base:bluetooth_address", "//internal/platform:comm", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep + "//internal/test/google3_only:test", "@boringssl//:crypto", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/functional:any_invocable", diff --git a/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc b/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc index b1ca0724..5283de18 100644 --- a/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc +++ b/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc @@ -44,17 +44,18 @@ FastPairPairerImpl::Factory* FastPairPairerImpl::Factory::g_test_factory_ = // static std::unique_ptr FastPairPairerImpl::Factory::Create( FastPairDevice& device, Mediums& medium, SingleThreadExecutor* executor, - OnPairedCallback on_paired_cb, OnPairingFailedCallback on_pair_failed_cb, + AccountManager* account_manager, OnPairedCallback on_paired_cb, + OnPairingFailedCallback on_pair_failed_cb, OnAccountKeyFailureCallback on_account_failure_cb, OnPairingCompletedCallback on_pairing_completed_cb) { if (g_test_factory_) { return g_test_factory_->CreateInstance( - device, medium, executor, std::move(on_paired_cb), + device, medium, executor, account_manager, std::move(on_paired_cb), std::move(on_pair_failed_cb), std::move(on_account_failure_cb), std::move(on_pairing_completed_cb)); } return std::make_unique( - device, medium, executor, std::move(on_paired_cb), + device, medium, executor, account_manager, std::move(on_paired_cb), std::move(on_pair_failed_cb), std::move(on_account_failure_cb), std::move(on_pairing_completed_cb)); } @@ -67,12 +68,14 @@ void FastPairPairerImpl::Factory::SetFactoryForTesting( FastPairPairerImpl::FastPairPairerImpl( FastPairDevice& device, Mediums& medium, SingleThreadExecutor* executor, - OnPairedCallback on_paired_cb, OnPairingFailedCallback on_pair_failed_cb, + AccountManager* account_manager, OnPairedCallback on_paired_cb, + OnPairingFailedCallback on_pair_failed_cb, OnAccountKeyFailureCallback on_account_failure_cb, OnPairingCompletedCallback on_pairing_completed_cb) : device_(device), mediums_(medium), executor_(executor), + account_manager_(account_manager), on_paired_cb_(std::move(on_paired_cb)), on_pair_failed_cb_(std::move(on_pair_failed_cb)), on_account_key_failure_cb_(std::move(on_account_failure_cb)), @@ -279,7 +282,13 @@ void FastPairPairerImpl::AttemptSendAccountKey() { return; } - // TODO(b/281781730) : Check if we need to send account key + if (!account_manager_->GetCurrentAccount().has_value()) { + NEARBY_LOGS(INFO) + << __func__ + << ": No need to write accountkey because no logged in user."; + NotifyPairingCompleted(); + return; + } // TODO(b/281782018) : Handle BLE address rotation fast_pair_gatt_service_client_->WriteAccountKey( *fast_pair_handshake_->fast_pair_data_encryptor(), diff --git a/fastpair/pairing/fastpair/fast_pair_pairer_impl.h b/fastpair/pairing/fastpair/fast_pair_pairer_impl.h index a3fe538b..c7d3e55c 100644 --- a/fastpair/pairing/fastpair/fast_pair_pairer_impl.h +++ b/fastpair/pairing/fastpair/fast_pair_pairer_impl.h @@ -26,6 +26,7 @@ #include "fastpair/handshake/fast_pair_handshake.h" #include "fastpair/internal/mediums/mediums.h" #include "fastpair/pairing/fastpair/fast_pair_pairer.h" +#include "internal/account/account_manager.h" #include "internal/platform/bluetooth_classic.h" #include "internal/platform/single_thread_executor.h" #include "internal/platform/timer_impl.h" @@ -39,7 +40,7 @@ class FastPairPairerImpl : public FastPairPairer { public: static std::unique_ptr Create( FastPairDevice& device, Mediums& medium, SingleThreadExecutor* executor, - OnPairedCallback on_paired_cb, + AccountManager* account_manager, OnPairedCallback on_paired_cb, OnPairingFailedCallback on_pair_failed_cb, OnAccountKeyFailureCallback on_account_failure_cb, OnPairingCompletedCallback on_pairing_completed_cb); @@ -51,7 +52,7 @@ class FastPairPairerImpl : public FastPairPairer { virtual std::unique_ptr CreateInstance( FastPairDevice& device, Mediums& medium, SingleThreadExecutor* executor, - OnPairedCallback on_paired_cb, + AccountManager* account_manager, OnPairedCallback on_paired_cb, OnPairingFailedCallback on_pair_failed_cb, OnAccountKeyFailureCallback on_account_failure_cb, OnPairingCompletedCallback on_pairing_completed_cb) = 0; @@ -62,6 +63,7 @@ class FastPairPairerImpl : public FastPairPairer { FastPairPairerImpl(FastPairDevice& device, Mediums& medium, SingleThreadExecutor* executor, + AccountManager* account_manager, OnPairedCallback on_paired_cb, OnPairingFailedCallback on_pair_failed_cb, OnAccountKeyFailureCallback on_account_failure_cb, @@ -103,6 +105,7 @@ class FastPairPairerImpl : public FastPairPairer { FastPairDevice& device_; Mediums& mediums_; SingleThreadExecutor* executor_; + AccountManager* account_manager_; OnPairedCallback on_paired_cb_ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); OnPairingFailedCallback on_pair_failed_cb_ ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); diff --git a/fastpair/pairing/fastpair/fast_pair_pairer_impl_test.cc b/fastpair/pairing/fastpair/fast_pair_pairer_impl_test.cc index 729d70c1..ccc740b9 100644 --- a/fastpair/pairing/fastpair/fast_pair_pairer_impl_test.cc +++ b/fastpair/pairing/fastpair/fast_pair_pairer_impl_test.cc @@ -34,6 +34,7 @@ #include "fastpair/common/account_key.h" #include "fastpair/common/device_metadata.h" #include "fastpair/common/fast_pair_device.h" +#include "fastpair/common/fast_pair_prefs.h" #include "fastpair/common/fast_pair_version.h" #include "fastpair/common/protocol.h" #include "fastpair/crypto/decrypted_passkey.h" @@ -46,12 +47,15 @@ #include "fastpair/pairing/fastpair/fast_pair_pairer.h" #include "fastpair/proto/fastpair_rpcs.proto.h" #include "fastpair/repository/fake_fast_pair_repository.h" +#include "internal/account/fake_account_manager.h" #include "internal/base/bluetooth_address.h" #include "internal/platform/ble_v2.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" #include "internal/platform/single_thread_executor.h" +#include "internal/platform/task_runner_impl.h" +#include "internal/test/google3_only/fake_authentication_manager.h" namespace nearby { namespace fastpair { @@ -62,6 +66,9 @@ using ::nearby::api::ble_v2::GattCharacteristic; using DiscoveryCallback = BluetoothClassicMedium::DiscoveryCallback; constexpr absl::Duration kWaitTimeout = absl::Milliseconds(200); +constexpr absl::string_view kFastPairPreferencesFilePath = + "Google/Nearby/FastPair"; +constexpr absl::string_view kTestAccountId = "test_account_id"; constexpr absl::string_view kMetadataId("718c17"); constexpr absl::string_view kPublicAntiSpoof = "Wuyr48lD3txnUhGiMF1IfzlTwRxxe+wMB1HLzP+" @@ -130,11 +137,18 @@ class FastPairPairerImplTest : public testing::Test { FastPairPairerImplTest() { FastPairDataEncryptorImpl::Factory::SetFactoryForTesting( &fake_data_encryptor_factory_); + task_runner_ = std::make_unique(1); + preferences_manager_ = std::make_unique( + kFastPairPreferencesFilePath); + authentication_manager_ = std::make_unique(); } void SetUp() override { env_.Start(); // Setups seeker device. mediums_ = std::make_unique(); + account_manager_ = std::make_unique( + preferences_manager_.get(), prefs::kNearbyFastPairUsersName, + authentication_manager_.get(), task_runner_.get()); // Setups provider device. adapter_provider_ = std::make_unique(); @@ -162,6 +176,7 @@ class FastPairPairerImplTest : public testing::Test { fast_pair_pairer_.reset(); FastPairHandshakeLookup::GetInstance()->Clear(); mediums_.reset(); + account_manager_.reset(); device_.reset(); remote_device_ = nullptr; key_based_characteristic_ = std::nullopt; @@ -178,6 +193,12 @@ class FastPairPairerImplTest : public testing::Test { env_.Stop(); } + void LogInAccount() { + AccountManager::Account account; + account.id = kTestAccountId; + account_manager_->SetAccount(account); + } + void CreateMockDevice(DeviceFastPairVersion version, Protocol protocol) { device_ = std::make_unique( kMetadataId, remote_device_->GetMacAddress(), protocol); @@ -354,6 +375,7 @@ class FastPairPairerImplTest : public testing::Test { std::unique_ptr fast_pair_pairer_; FastPairFakeDataEncryptorImplFactory fake_data_encryptor_factory_; SingleThreadExecutor executor_; + std::unique_ptr account_manager_; std::optional key_based_characteristic_; std::optional passkey_characteristic_; std::optional accountkey_characteristic_; @@ -362,6 +384,9 @@ class FastPairPairerImplTest : public testing::Test { private: MediumEnvironment& env_{MediumEnvironment::Instance()}; Mutex mutex_; + std::unique_ptr preferences_manager_; + std::unique_ptr authentication_manager_; + std::unique_ptr task_runner_; std::unique_ptr bt_provider_; std::unique_ptr adapter_provider_; std::unique_ptr gatt_server_; @@ -375,6 +400,7 @@ class FastPairPairerImplTest : public testing::Test { TEST_F(FastPairPairerImplTest, SuccessInitialPairingWithDeviceVersionHigherThanV1) { + LogInAccount(); auto repository = std::make_unique(); repository->SetResultOfWriteAccountAssociationToFootprints(absl::OkStatus()); ConfigurePairingContext(); @@ -393,7 +419,7 @@ TEST_F(FastPairPairerImplTest, EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { paired_latch.CountDown(); }, [&](FastPairDevice& device, PairFailure failure) { FAIL() << "Unexpected pairing failure " << failure; @@ -414,6 +440,7 @@ TEST_F(FastPairPairerImplTest, } TEST_F(FastPairPairerImplTest, SuccessInitialPairingWithDeviceV1) { + LogInAccount(); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kV1, @@ -424,7 +451,7 @@ TEST_F(FastPairPairerImplTest, SuccessInitialPairingWithDeviceV1) { CountDownLatch complete_latch(1); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { paired_latch.CountDown(); }, [&](FastPairDevice& device, PairFailure failure) { FAIL() << "Unexpected pairing failure " << failure; @@ -444,6 +471,7 @@ TEST_F(FastPairPairerImplTest, SuccessInitialPairingWithDeviceV1) { } TEST_F(FastPairPairerImplTest, SuccessSubsequentPairingWithDevice) { + LogInAccount(); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, @@ -459,7 +487,7 @@ TEST_F(FastPairPairerImplTest, SuccessSubsequentPairingWithDevice) { CountDownLatch complete_latch(1); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { paired_latch.CountDown(); }, [&](FastPairDevice& device, PairFailure failure) { FAIL() << "Unexpected pairing failure " << failure; @@ -479,6 +507,7 @@ TEST_F(FastPairPairerImplTest, SuccessSubsequentPairingWithDevice) { } TEST_F(FastPairPairerImplTest, SuccessRetroactivePairingWithDevice) { + LogInAccount(); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, @@ -493,7 +522,7 @@ TEST_F(FastPairPairerImplTest, SuccessRetroactivePairingWithDevice) { EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { FAIL() << "Unexpected callback"; }, [&](FastPairDevice& device, PairFailure failure) { FAIL() << "Unexpected pairing failure " << failure; @@ -523,7 +552,7 @@ TEST_F(FastPairPairerImplTest, FailedToUnPair) { EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { FAIL() << "Unexpected callback"; }, [&](FastPairDevice& device, PairFailure failure) { EXPECT_EQ(failure, PairFailure::kPairingAndConnect); @@ -561,7 +590,7 @@ TEST_F(FastPairPairerImplTest, FailedToPairingWithAuthTimeout) { EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { FAIL() << "Unexpected callback"; }, [&](FastPairDevice& device, PairFailure failure) { EXPECT_EQ(failure, PairFailure::kPairingAndConnect); @@ -594,7 +623,7 @@ TEST_F(FastPairPairerImplTest, NoPasskeyResponse) { EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { FAIL() << "Unexpected callback"; }, [&](FastPairDevice& device, PairFailure failure) { EXPECT_EQ(failure, PairFailure::kPasskeyResponseTimeout); @@ -629,7 +658,7 @@ TEST_F(FastPairPairerImplTest, PasskeyMismatch) { EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { FAIL() << "Unexpected callback"; }, [&](FastPairDevice& device, PairFailure failure) { EXPECT_EQ(failure, PairFailure::kPasskeyMismatch); @@ -665,7 +694,7 @@ TEST_F(FastPairPairerImplTest, ReceiveWithWrongPasskeyResponse) { EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { FAIL() << "Unexpected callback"; }, [&](FastPairDevice& device, PairFailure failure) { EXPECT_EQ(failure, PairFailure::kPasskeyDecryptFailure); @@ -702,7 +731,7 @@ TEST_F(FastPairPairerImplTest, ReceiveWithWrongPasskeyMessageType) { EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { FAIL() << "Unexpected callback"; }, [&](FastPairDevice& device, PairFailure failure) { EXPECT_EQ(failure, PairFailure::kIncorrectPasskeyResponseType); @@ -720,8 +749,46 @@ TEST_F(FastPairPairerImplTest, ReceiveWithWrongPasskeyMessageType) { EXPECT_FALSE(device_->GetAccountKey().Ok()); } +TEST_F(FastPairPairerImplTest, SkipWriteAccountKeyBecauseNoLoggedInUser) { + ConfigurePairingContext(); + SetPairingResult(std::nullopt); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairInitialPairing); + SetupProviderGattServer(); + SetNotifyResponse(*key_based_characteristic_, kKeyBasedResponse); + SetNotifyResponse(*passkey_characteristic_, kPasskeyResponse); + SetDecryptedResponse(); + SetDecryptedPasskey(); + CreateFastPairHandshakeInstanceForDevice(); + + CountDownLatch paired_latch(1); + CountDownLatch complete_latch(1); + + EXPECT_FALSE(device_->GetAccountKey().Ok()); + + fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( + *device_, *mediums_, &executor_, account_manager_.get(), + [&](FastPairDevice& cb_device) { paired_latch.CountDown(); }, + [&](FastPairDevice& device, PairFailure failure) { + FAIL() << "Unexpected pairing failure " << failure; + }, + [&](FastPairDevice& device, PairFailure failure) { + FAIL() << "Unexpected pairing failure " << failure; + }, + [&](FastPairDevice& device) { + EXPECT_FALSE(device.GetAccountKey().Ok()); + complete_latch.CountDown(); + }); + fast_pair_pairer_->StartPairing(); + paired_latch.Await(); + complete_latch.Await(kWaitTimeout).result(); + EXPECT_TRUE(fast_pair_pairer_->IsPaired()); + EXPECT_FALSE(device_->GetAccountKey().Ok()); +} + TEST_F(FastPairPairerImplTest, SuccessPairingWithDeviceButFailedToWriteAccountkeyToRemoteDevice) { + LogInAccount(); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, @@ -742,7 +809,7 @@ TEST_F(FastPairPairerImplTest, EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { paired_latch.CountDown(); }, [&](FastPairDevice& device, PairFailure failure) { failure_latch.CountDown(); @@ -766,6 +833,7 @@ TEST_F(FastPairPairerImplTest, TEST_F(FastPairPairerImplTest, SuccessPairingWithDeviceButFailedToWriteAccountkeyToFootprints) { + LogInAccount(); auto repository = std::make_unique(); repository->SetResultOfWriteAccountAssociationToFootprints( absl::InternalError("Failed to write account key to foot prints")); @@ -785,7 +853,7 @@ TEST_F(FastPairPairerImplTest, EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { paired_latch.CountDown(); }, [&](FastPairDevice& device, PairFailure failure) { FAIL() << "Unexpected pairing failure " << failure; @@ -819,7 +887,7 @@ TEST_F(FastPairPairerImplTest, TestCancelPairing) { EXPECT_FALSE(device_->GetAccountKey().Ok()); fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( - *device_, *mediums_, &executor_, + *device_, *mediums_, &executor_, account_manager_.get(), [&](FastPairDevice& cb_device) { FAIL() << "Unexpected callback"; }, [&](FastPairDevice& device, PairFailure failure) { EXPECT_EQ(failure, PairFailure::kPairingAndConnect); diff --git a/fastpair/pairing/pairer_broker_impl.cc b/fastpair/pairing/pairer_broker_impl.cc index 8fa67982..eca58aaa 100644 --- a/fastpair/pairing/pairer_broker_impl.cc +++ b/fastpair/pairing/pairer_broker_impl.cc @@ -36,8 +36,9 @@ constexpr absl::Duration kRetryHandshakeDelay = absl::Seconds(1); } // namespace PairerBrokerImpl::PairerBrokerImpl(Mediums& medium, - SingleThreadExecutor* executor) - : medium_(medium), executor_(executor) {} + SingleThreadExecutor* executor, + AccountManager* account_manager) + : medium_(medium), executor_(executor), account_manager_(account_manager) {} void PairerBrokerImpl::AddObserver(Observer* observer) { observers_.AddObserver(observer); @@ -191,7 +192,7 @@ void PairerBrokerImpl::StartPairingAttempt(FastPairDevice& device) { MutexLock lock(&mutex_); // Create FastPairPairer instance and start pairing. fast_pair_pairers_[device.GetModelId()] = FastPairPairerImpl::Factory::Create( - device, medium_, executor_, + device, medium_, executor_, account_manager_, [&](FastPairDevice& cb_device) { executor_->Execute("OnFastPairDevicePaired", [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { @@ -298,10 +299,11 @@ void PairerBrokerImpl::OnFastPairProcedureComplete(FastPairDevice& device) { // been written for devices with a version of V2 or higher. if (device.GetVersion().has_value() && device.GetVersion().value() == DeviceFastPairVersion::kHigherThanV1 && + device.GetAccountKey().Ok() && (device.GetProtocol() == Protocol::kFastPairInitialPairing || device.GetProtocol() == Protocol::kFastPairRetroactivePairing)) { for (auto& observer : observers_.GetObservers()) { - observer->OnAccountKeyWrite(device, /*error=*/absl::nullopt); + observer->OnAccountKeyWrite(device, /*error=*/std::nullopt); } } } diff --git a/fastpair/pairing/pairer_broker_impl.h b/fastpair/pairing/pairer_broker_impl.h index 0da1e316..0c88d975 100644 --- a/fastpair/pairing/pairer_broker_impl.h +++ b/fastpair/pairing/pairer_broker_impl.h @@ -24,6 +24,7 @@ #include "fastpair/internal/mediums/mediums.h" #include "fastpair/pairing/fastpair/fast_pair_pairer.h" #include "fastpair/pairing/pairer_broker.h" +#include "internal/account/account_manager.h" #include "internal/base/observer_list.h" #include "internal/platform/single_thread_executor.h" #include "internal/platform/timer_impl.h" @@ -33,7 +34,8 @@ namespace fastpair { class PairerBrokerImpl : public PairerBroker { public: - explicit PairerBrokerImpl(Mediums& medium, SingleThreadExecutor* executor); + explicit PairerBrokerImpl(Mediums& medium, SingleThreadExecutor* executor, + AccountManager* account_manager); PairerBrokerImpl(const PairerBrokerImpl&) = delete; PairerBrokerImpl& operator=(const PairerBrokerImpl&) = delete; @@ -74,6 +76,7 @@ class PairerBrokerImpl : public PairerBroker { Mediums& medium_; SingleThreadExecutor* executor_; + AccountManager* account_manager_; // The key for all the following maps is a device model id. absl::flat_hash_map> diff --git a/fastpair/pairing/pairer_broker_impl_test.cc b/fastpair/pairing/pairer_broker_impl_test.cc index 9db64e52..e8688d97 100644 --- a/fastpair/pairing/pairer_broker_impl_test.cc +++ b/fastpair/pairing/pairer_broker_impl_test.cc @@ -24,6 +24,7 @@ #include "gtest/gtest.h" #include "absl/functional/bind_front.h" #include "fastpair//handshake/fast_pair_handshake_lookup.h" +#include "fastpair/common/fast_pair_prefs.h" #include "fastpair/common/pair_failure.h" #include "fastpair/crypto/decrypted_passkey.h" #include "fastpair/crypto/decrypted_response.h" @@ -36,10 +37,13 @@ #include "fastpair/internal/mediums/mediums.h" #include "fastpair/proto/fastpair_rpcs.proto.h" #include "fastpair/repository/fake_fast_pair_repository.h" +#include "internal/account/fake_account_manager.h" #include "internal/base/bluetooth_address.h" #include "internal/platform/ble_v2.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" +#include "internal/platform/task_runner_impl.h" +#include "internal/test/google3_only/fake_authentication_manager.h" namespace nearby { namespace fastpair { @@ -49,6 +53,9 @@ using Permission = nearby::api::ble_v2::GattCharacteristic::Permission; using ::nearby::api::ble_v2::GattCharacteristic; using DiscoveryCallback = BluetoothClassicMedium::DiscoveryCallback; +constexpr absl::string_view kFastPairPreferencesFilePath = + "Google/Nearby/FastPair"; +constexpr absl::string_view kTestAccountId = "test_account_id"; constexpr absl::string_view kMetadataId("718c17"); constexpr absl::string_view kPublicAntiSpoof = "Wuyr48lD3txnUhGiMF1IfzlTwRxxe+wMB1HLzP+" @@ -173,12 +180,19 @@ class PairerBrokerImplTest : public testing::Test { PairerBrokerImplTest() { FastPairDataEncryptorImpl::Factory::SetFactoryForTesting( &fake_data_encryptor_factory_); + task_runner_ = std::make_unique(1); + preferences_manager_ = std::make_unique( + kFastPairPreferencesFilePath); + authentication_manager_ = std::make_unique(); } void SetUp() override { env_.Start(); // Setups seeker device. mediums_ = std::make_unique(); + account_manager_ = std::make_unique( + preferences_manager_.get(), prefs::kNearbyFastPairUsersName, + authentication_manager_.get(), task_runner_.get()); // Setups provider device. adapter_provider_ = std::make_unique(); @@ -205,6 +219,7 @@ class PairerBrokerImplTest : public testing::Test { pairer_broker_.reset(); FastPairHandshakeLookup::GetInstance()->Clear(); mediums_.reset(); + account_manager_.reset(); device_.reset(); remote_device_ = nullptr; key_based_characteristic_ = std::nullopt; @@ -220,6 +235,12 @@ class PairerBrokerImplTest : public testing::Test { env_.Stop(); } + void LogInAccount() { + AccountManager::Account account; + account.id = kTestAccountId; + account_manager_->SetAccount(account); + } + void CreateMockDevice(DeviceFastPairVersion version, Protocol protocol) { device_ = std::make_unique( kMetadataId, remote_device_->GetMacAddress(), protocol); @@ -385,6 +406,7 @@ class PairerBrokerImplTest : public testing::Test { std::unique_ptr device_; BluetoothDevice* remote_device_ = nullptr; SingleThreadExecutor executor_; + std::unique_ptr account_manager_; std::unique_ptr pairer_broker_; std::optional key_based_characteristic_; std::optional passkey_characteristic_; @@ -394,6 +416,9 @@ class PairerBrokerImplTest : public testing::Test { private: MediumEnvironment& env_{MediumEnvironment::Instance()}; Mutex mutex_; + std::unique_ptr preferences_manager_; + std::unique_ptr authentication_manager_; + std::unique_ptr task_runner_; std::unique_ptr bt_provider_; std::unique_ptr adapter_provider_; std::unique_ptr gatt_server_; @@ -405,6 +430,7 @@ class PairerBrokerImplTest : public testing::Test { }; TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDeviceV1) { + LogInAccount(); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kV1, @@ -415,7 +441,8 @@ TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDeviceV1) { CountDownLatch account_key_writed_latch(1); CountDownLatch pairing_completed_latch(1); CountDownLatch pairing_failure_latch(1); - pairer_broker_ = std::make_unique(*mediums_, &executor_); + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); PairerBrokerObserver pairer_broker_observer( pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, &pairing_completed_latch, &pairing_failure_latch); @@ -428,6 +455,7 @@ TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDeviceV1) { } TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDevice) { + LogInAccount(); auto repository = std::make_unique(); repository->SetResultOfWriteAccountAssociationToFootprints(absl::OkStatus()); ConfigurePairingContext(); @@ -448,7 +476,8 @@ TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDevice) { CountDownLatch pairing_completed_latch(1); CountDownLatch pairing_failure_latch(1); - pairer_broker_ = std::make_unique(*mediums_, &executor_); + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); PairerBrokerObserver pairer_broker_observer( pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, &pairing_completed_latch, &pairing_failure_latch); @@ -465,6 +494,7 @@ TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDevice) { } TEST_F(PairerBrokerImplTest, SuccessSubsequentPairingWithDevice) { + LogInAccount(); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, @@ -480,7 +510,8 @@ TEST_F(PairerBrokerImplTest, SuccessSubsequentPairingWithDevice) { CountDownLatch pairing_completed_latch(1); CountDownLatch pairing_failure_latch(1); - pairer_broker_ = std::make_unique(*mediums_, &executor_); + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); PairerBrokerObserver pairer_broker_observer( pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, &pairing_completed_latch, &pairing_failure_latch); @@ -494,6 +525,7 @@ TEST_F(PairerBrokerImplTest, SuccessSubsequentPairingWithDevice) { } TEST_F(PairerBrokerImplTest, SuccessRetroactivePairingWithDevice) { + LogInAccount(); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, @@ -509,7 +541,8 @@ TEST_F(PairerBrokerImplTest, SuccessRetroactivePairingWithDevice) { EXPECT_FALSE(device_->GetAccountKey().Ok()); - pairer_broker_ = std::make_unique(*mediums_, &executor_); + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); PairerBrokerObserver pairer_broker_observer( pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, &pairing_completed_latch, &pairing_failure_latch); @@ -533,7 +566,8 @@ TEST_F(PairerBrokerImplTest, FaileToCreateHandshakeRetryThreeTimes) { CountDownLatch pairing_completed_latch(1); CountDownLatch pairing_failure_latch(1); - pairer_broker_ = std::make_unique(*mediums_, &executor_); + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); PairerBrokerObserver pairer_broker_observer( pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, &pairing_completed_latch, &pairing_failure_latch); @@ -549,7 +583,41 @@ TEST_F(PairerBrokerImplTest, FaileToCreateHandshakeRetryThreeTimes) { PairFailure::kKeyBasedPairingResponseTimeout); } +TEST_F(PairerBrokerImplTest, SkipWriteAccountKeyBecauseNoLoggedInUser) { + ConfigurePairingContext(); + SetPairingResult(std::nullopt); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairInitialPairing); + SetupProviderGattServer(); + SetNotifyResponse(*key_based_characteristic_, kKeyBasedResponse); + SetNotifyResponse(*passkey_characteristic_, kPasskeyResponse); + SetDecryptedResponse(); + SetDecryptedPasskey(); + CreateFastPairHandshakeInstanceForDevice(); + + CountDownLatch device_paired_latch(1); + CountDownLatch account_key_writed_latch(1); + CountDownLatch pairing_completed_latch(1); + CountDownLatch pairing_failure_latch(1); + + EXPECT_FALSE(device_->GetAccountKey().Ok()); + + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); + PairerBrokerObserver pairer_broker_observer( + pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, + &pairing_completed_latch, &pairing_failure_latch); + pairer_broker_->PairDevice(*device_); + + device_paired_latch.Await(); + pairing_completed_latch.Await(); + EXPECT_FALSE(account_key_writed_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(pairing_failure_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(device_->GetAccountKey().Ok()); +} + TEST_F(PairerBrokerImplTest, FaileToWriteAccountkeyToRemoteDevice) { + LogInAccount(); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, @@ -569,7 +637,8 @@ TEST_F(PairerBrokerImplTest, FaileToWriteAccountkeyToRemoteDevice) { EXPECT_FALSE(device_->GetAccountKey().Ok()); - pairer_broker_ = std::make_unique(*mediums_, &executor_); + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); PairerBrokerObserver pairer_broker_observer( pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, &pairing_completed_latch, &pairing_failure_latch); @@ -585,6 +654,7 @@ TEST_F(PairerBrokerImplTest, FaileToWriteAccountkeyToRemoteDevice) { } TEST_F(PairerBrokerImplTest, FaileToWriteAccountkeyToFootprints) { + LogInAccount(); auto repository = std::make_unique(); repository->SetResultOfWriteAccountAssociationToFootprints( absl::InternalError("Failed to write account key to foot prints")); @@ -606,7 +676,8 @@ TEST_F(PairerBrokerImplTest, FaileToWriteAccountkeyToFootprints) { EXPECT_FALSE(device_->GetAccountKey().Ok()); - pairer_broker_ = std::make_unique(*mediums_, &executor_); + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); PairerBrokerObserver pairer_broker_observer( pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, &pairing_completed_latch, &pairing_failure_latch); @@ -622,6 +693,7 @@ TEST_F(PairerBrokerImplTest, FaileToWriteAccountkeyToFootprints) { } TEST_F(PairerBrokerImplTest, FailToPairRetryThreeTimes) { + LogInAccount(); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, @@ -640,7 +712,8 @@ TEST_F(PairerBrokerImplTest, FailToPairRetryThreeTimes) { EXPECT_FALSE(device_->GetAccountKey().Ok()); - pairer_broker_ = std::make_unique(*mediums_, &executor_); + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); PairerBrokerObserver pairer_broker_observer( pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, &pairing_completed_latch, &pairing_failure_latch); From c6d96b59714d1fe17436679a192dcba5f52ce5bd Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Tue, 25 Jul 2023 17:19:32 -0700 Subject: [PATCH 010/128] Fix minor defects Fixes use-after-free and NPE errors. PiperOrigin-RevId: 551042892 --- .../implementation/p2p_cluster_pcp_handler.cc | 141 +++++++++--------- internal/platform/medium_environment.cc | 2 +- 2 files changed, 71 insertions(+), 72 deletions(-) diff --git a/connections/implementation/p2p_cluster_pcp_handler.cc b/connections/implementation/p2p_cluster_pcp_handler.cc index ba7155d5..ab6491f8 100644 --- a/connections/implementation/p2p_cluster_pcp_handler.cc +++ b/connections/implementation/p2p_cluster_pcp_handler.cc @@ -298,78 +298,77 @@ void P2pClusterPcpHandler::BluetoothNameChangedHandler( BluetoothDevice device) { RunOnPcpHandlerThread( "p2p-bt-name-changed", - [this, client, service_id, device]() - RUN_ON_PCP_HANDLER_THREAD() { - // Make sure we are still discovering before proceeding. - if (!client->IsDiscovering()) { - NEARBY_LOGS(WARNING) - << "Ignoring lost BluetoothDevice " << device.GetName() - << " because Connections is no longer discovering."; - return; - } + [this, client, service_id, device]() RUN_ON_PCP_HANDLER_THREAD() { + // Make sure we are still discovering before proceeding. + if (!client->IsDiscovering()) { + NEARBY_LOGS(WARNING) + << "Ignoring lost BluetoothDevice " << device.GetName() + << " because Connections is no longer discovering."; + return; + } - // Parse the Bluetooth device name. - const std::string device_name_string = device.GetName(); - BluetoothDeviceName device_name(device_name_string); - NEARBY_LOGS(INFO) - << "BT discovery handler (CHANGED) [client_id=" - << client->GetClientId() << ", service_id=" << service_id - << "]: processing new name " << device_name_string; + // Parse the Bluetooth device name. + const std::string device_name_string = device.GetName(); + BluetoothDeviceName device_name(device_name_string); + NEARBY_LOGS(INFO) << "BT discovery handler (CHANGED) [client_id=" + << client->GetClientId() + << ", service_id=" << service_id + << "]: processing new name " << device_name_string; - // By this point, the BluetoothDevice passed to us has a different - // name than what we may have discovered before. We need to iterate - // over the found BluetoothEndpoints and compare their addresses to - // see the devices are the same. We are not guaranteed to discover a - // match, since the old name may not have been formatted for Nearby - // Connections. - for (auto endpoint : GetDiscoveredEndpoints(Medium::BLUETOOTH)) { - BluetoothEndpoint* bluetoothEndpoint = - static_cast(endpoint); - NEARBY_LOGS(INFO) - << "BT discovery handler (CHANGED) [client_id=" - << client->GetClientId() << ", service_id=" << service_id - << "]: comparing MAC addresses with existing endpoint " - << bluetoothEndpoint->bluetooth_device.GetName() - << ". They have MAC address " - << bluetoothEndpoint->bluetooth_device.GetMacAddress() - << " and the new endpoint has MAC address " - << device.GetMacAddress(); - if (bluetoothEndpoint->bluetooth_device.GetMacAddress() == - device.GetMacAddress()) { - // Report the BluetoothEndpoint as lost to the client. - NEARBY_LOGS(INFO) - << "Reporting lost BluetoothDevice " - << bluetoothEndpoint->bluetooth_device.GetName() - << ", due to device name change."; - OnEndpointLost(client, *endpoint); - break; - } - } + // By this point, the BluetoothDevice passed to us has a different + // name than what we may have discovered before. We need to iterate + // over the found BluetoothEndpoints and compare their addresses to + // see the devices are the same. We are not guaranteed to discover a + // match, since the old name may not have been formatted for Nearby + // Connections. + for (auto endpoint : GetDiscoveredEndpoints(Medium::BLUETOOTH)) { + BluetoothEndpoint* bluetoothEndpoint = + static_cast(endpoint); + NEARBY_LOGS(INFO) + << "BT discovery handler (CHANGED) [client_id=" + << client->GetClientId() << ", service_id=" << service_id + << "]: comparing MAC addresses with existing endpoint " + << bluetoothEndpoint->bluetooth_device.GetName() + << ". They have MAC address " + << bluetoothEndpoint->bluetooth_device.GetMacAddress() + << " and the new endpoint has MAC address " + << device.GetMacAddress(); + if (bluetoothEndpoint->bluetooth_device.GetMacAddress() == + device.GetMacAddress()) { + // Report the BluetoothEndpoint as lost to the client. + NEARBY_LOGS(INFO) << "Reporting lost BluetoothDevice " + << bluetoothEndpoint->bluetooth_device.GetName() + << ", due to device name change."; + OnEndpointLost(client, *endpoint); + break; + } + } - // Make sure the Bluetooth device name points to a valid - // endpoint we're discovering. - if (!IsRecognizedBluetoothEndpoint(device_name_string, service_id, - device_name)) { - NEARBY_LOGS(INFO) << "Found unrecognized BluetoothDeviceName " - << device_name_string; - return; - } + // Make sure the Bluetooth device name points to a valid + // endpoint we're discovering. + if (!IsRecognizedBluetoothEndpoint(device_name_string, service_id, + device_name)) { + NEARBY_LOGS(INFO) << "Found unrecognized BluetoothDeviceName " + << device_name_string; + return; + } - // Report the discovered endpoint to the client. - NEARBY_LOGS(INFO) - << "Found BluetoothDeviceName " << device_name_string - << " (with endpoint_id=" << device_name.GetEndpointId() - << " and endpoint_info=" - << absl::BytesToHexString(device_name.GetEndpointInfo().data()) - << ")."; - OnEndpointFound( - client, std::make_shared(BluetoothEndpoint{ - {device_name.GetEndpointId(), - device_name.GetEndpointInfo(), service_id, - Medium::BLUETOOTH, device_name.GetWebRtcState()}, - device, - })); - }); + // Report the discovered endpoint to the client. + NEARBY_LOGS(INFO) << "Found BluetoothDeviceName " << device_name_string + << " (with endpoint_id=" + << device_name.GetEndpointId() + << " and endpoint_info=" + << absl::BytesToHexString( + device_name.GetEndpointInfo().data()) + << ")."; + OnEndpointFound( + client, + std::make_shared(BluetoothEndpoint{ + {device_name.GetEndpointId(), device_name.GetEndpointInfo(), + service_id, Medium::BLUETOOTH, device_name.GetWebRtcState()}, + device, + })); + }); } void P2pClusterPcpHandler::BluetoothDeviceLostHandler( @@ -2165,9 +2164,6 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::WifiLanConnectImpl( WifiLanSocket socket = wifi_lan_medium_.Connect( endpoint->service_id, endpoint->service_info, client->GetCancellationFlag(endpoint->endpoint_id)); - NEARBY_LOGS(INFO) << "In WifiLanConnectImpl(), connect to service " - << " socket=" << &socket.GetImpl() - << " for endpoint(id=" << endpoint->endpoint_id << ")."; if (!socket.IsValid()) { NEARBY_LOGS(ERROR) << "In WifiLanConnectImpl(), failed to connect to service " @@ -2177,6 +2173,9 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::WifiLanConnectImpl( .status = {Status::kWifiLanError}, }; } + NEARBY_LOGS(INFO) << "In WifiLanConnectImpl(), connect to service " + << " socket=" << &socket.GetImpl() + << " for endpoint(id=" << endpoint->endpoint_id << ")."; auto channel = std::make_unique( endpoint->service_id, /*channel_name=*/endpoint->endpoint_id, socket); diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index c3395776..a5831ccc 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -936,8 +936,8 @@ api::WifiLanMedium* MediumEnvironment::GetWifiLanMedium( return; } } - latch.CountDown(); } + latch.CountDown(); }); latch.Await(); return result; From bccdf6543e10ab78e291e6fa4867a126f06ff499 Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Tue, 25 Jul 2023 17:32:02 -0700 Subject: [PATCH 011/128] Checks if a device is already saved before writing accountkey PiperOrigin-RevId: 551045458 --- .../pairing/fastpair/fast_pair_pairer_impl.cc | 25 ++++++- .../pairing/fastpair/fast_pair_pairer_impl.h | 1 + .../fastpair/fast_pair_pairer_impl_test.cc | 46 ++++++++++++ fastpair/pairing/pairer_broker_impl_test.cc | 41 ++++++++++ .../repository/fake_fast_pair_repository.cc | 16 +++- .../repository/fake_fast_pair_repository.h | 15 ++-- fastpair/repository/fast_pair_repository.h | 14 ++-- .../repository/fast_pair_repository_impl.cc | 51 ++++++++++++- .../repository/fast_pair_repository_impl.h | 12 +-- .../fast_pair_repository_impl_test.cc | 75 +++++++++++++++++++ 10 files changed, 276 insertions(+), 20 deletions(-) diff --git a/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc b/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc index 5283de18..e2c21138 100644 --- a/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc +++ b/fastpair/pairing/fastpair/fast_pair_pairer_impl.cc @@ -289,7 +289,30 @@ void FastPairPairerImpl::AttemptSendAccountKey() { NotifyPairingCompleted(); return; } - // TODO(b/281782018) : Handle BLE address rotation + + // It's possible that the user has opted to initial pair to a device that + // already has an account key saved. We check to see if this is the case + // before writing a new account key. + if (device_.GetProtocol() == Protocol::kFastPairInitialPairing) { + FastPairRepository::Get()->IsDeviceSavedToAccount( + device_.GetPublicAddress().value(), [this](absl::Status status) { + if (status.ok()) { + NEARBY_LOGS(VERBOSE) + << __func__ + << ": Device is already saved, skipping write account key. " + "Pairing procedure complete."; + NotifyPairingCompleted(); + return; + } + WriteAccountKey(); + }); + } else { + // TODO(b/281782018) : Handle BLE address rotation + WriteAccountKey(); + } +} + +void FastPairPairerImpl::WriteAccountKey() { fast_pair_gatt_service_client_->WriteAccountKey( *fast_pair_handshake_->fast_pair_data_encryptor(), [&](const std::optional account_key, diff --git a/fastpair/pairing/fastpair/fast_pair_pairer_impl.h b/fastpair/pairing/fastpair/fast_pair_pairer_impl.h index c7d3e55c..9024e636 100644 --- a/fastpair/pairing/fastpair/fast_pair_pairer_impl.h +++ b/fastpair/pairing/fastpair/fast_pair_pairer_impl.h @@ -88,6 +88,7 @@ class FastPairPairerImpl : public FastPairPairer { // Attempts to write account key to remote device void AttemptSendAccountKey() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + void WriteAccountKey(); // FastPairDataEncryptor::WriteAccountKey callback void OnWriteAccountKey(std::optional account_key, std::optional failure); diff --git a/fastpair/pairing/fastpair/fast_pair_pairer_impl_test.cc b/fastpair/pairing/fastpair/fast_pair_pairer_impl_test.cc index ccc740b9..d2eac0e5 100644 --- a/fastpair/pairing/fastpair/fast_pair_pairer_impl_test.cc +++ b/fastpair/pairing/fastpair/fast_pair_pairer_impl_test.cc @@ -402,6 +402,8 @@ TEST_F(FastPairPairerImplTest, SuccessInitialPairingWithDeviceVersionHigherThanV1) { LogInAccount(); auto repository = std::make_unique(); + repository->SetResultOfIsDeviceSavedToAccount( + absl::NotFoundError("not found")); repository->SetResultOfWriteAccountAssociationToFootprints(absl::OkStatus()); ConfigurePairingContext(); SetPairingResult(std::nullopt); @@ -786,9 +788,51 @@ TEST_F(FastPairPairerImplTest, SkipWriteAccountKeyBecauseNoLoggedInUser) { EXPECT_FALSE(device_->GetAccountKey().Ok()); } +TEST_F(FastPairPairerImplTest, + SkipWriteAccountKeyBecauseDeviceAlreadySavedToAccount) { + LogInAccount(); + auto repository = std::make_unique(); + repository->SetResultOfIsDeviceSavedToAccount(absl::OkStatus()); + ConfigurePairingContext(); + SetPairingResult(std::nullopt); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairInitialPairing); + SetupProviderGattServer(); + SetNotifyResponse(*key_based_characteristic_, kKeyBasedResponse); + SetNotifyResponse(*passkey_characteristic_, kPasskeyResponse); + SetDecryptedResponse(); + SetDecryptedPasskey(); + CreateFastPairHandshakeInstanceForDevice(); + + CountDownLatch paired_latch(1); + CountDownLatch complete_latch(1); + + fast_pair_pairer_ = FastPairPairerImpl::Factory::Create( + *device_, *mediums_, &executor_, account_manager_.get(), + [&](FastPairDevice& cb_device) { paired_latch.CountDown(); }, + [&](FastPairDevice& device, PairFailure failure) { + FAIL() << "Unexpected pairing failure " << failure; + }, + [&](FastPairDevice& device, PairFailure failure) { + FAIL() << "Unexpected pairing failure " << failure; + }, + [&](FastPairDevice& device) { + EXPECT_FALSE(device.GetAccountKey().Ok()); + complete_latch.CountDown(); + }); + fast_pair_pairer_->StartPairing(); + paired_latch.Await(); + complete_latch.Await(); + EXPECT_TRUE(fast_pair_pairer_->IsPaired()); + EXPECT_FALSE(device_->GetAccountKey().Ok()); +} + TEST_F(FastPairPairerImplTest, SuccessPairingWithDeviceButFailedToWriteAccountkeyToRemoteDevice) { LogInAccount(); + auto repository = std::make_unique(); + repository->SetResultOfIsDeviceSavedToAccount( + absl::NotFoundError("not found")); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, @@ -835,6 +879,8 @@ TEST_F(FastPairPairerImplTest, SuccessPairingWithDeviceButFailedToWriteAccountkeyToFootprints) { LogInAccount(); auto repository = std::make_unique(); + repository->SetResultOfIsDeviceSavedToAccount( + absl::NotFoundError("not found")); repository->SetResultOfWriteAccountAssociationToFootprints( absl::InternalError("Failed to write account key to foot prints")); ConfigurePairingContext(); diff --git a/fastpair/pairing/pairer_broker_impl_test.cc b/fastpair/pairing/pairer_broker_impl_test.cc index e8688d97..d1d98648 100644 --- a/fastpair/pairing/pairer_broker_impl_test.cc +++ b/fastpair/pairing/pairer_broker_impl_test.cc @@ -457,6 +457,8 @@ TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDeviceV1) { TEST_F(PairerBrokerImplTest, SuccessInitialPairingWithDevice) { LogInAccount(); auto repository = std::make_unique(); + repository->SetResultOfIsDeviceSavedToAccount( + absl::NotFoundError("not found")); repository->SetResultOfWriteAccountAssociationToFootprints(absl::OkStatus()); ConfigurePairingContext(); SetPairingResult(std::nullopt); @@ -616,8 +618,45 @@ TEST_F(PairerBrokerImplTest, SkipWriteAccountKeyBecauseNoLoggedInUser) { EXPECT_FALSE(device_->GetAccountKey().Ok()); } +TEST_F(PairerBrokerImplTest, + SkipWriteAccountKeyBecauseDeviceAlreadySavedToAccount) { + LogInAccount(); + auto repository = std::make_unique(); + repository->SetResultOfIsDeviceSavedToAccount(absl::OkStatus()); + ConfigurePairingContext(); + SetPairingResult(std::nullopt); + CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, + Protocol::kFastPairInitialPairing); + SetupProviderGattServer(); + SetNotifyResponse(*key_based_characteristic_, kKeyBasedResponse); + SetNotifyResponse(*passkey_characteristic_, kPasskeyResponse); + SetDecryptedResponse(); + SetDecryptedPasskey(); + CreateFastPairHandshakeInstanceForDevice(); + + CountDownLatch device_paired_latch(1); + CountDownLatch account_key_writed_latch(1); + CountDownLatch pairing_completed_latch(1); + CountDownLatch pairing_failure_latch(1); + + pairer_broker_ = std::make_unique(*mediums_, &executor_, + account_manager_.get()); + PairerBrokerObserver pairer_broker_observer( + pairer_broker_.get(), &device_paired_latch, &account_key_writed_latch, + &pairing_completed_latch, &pairing_failure_latch); + pairer_broker_->PairDevice(*device_); + + device_paired_latch.Await(); + pairing_completed_latch.Await(); + EXPECT_FALSE(account_key_writed_latch.Await(kWaitTimeout).result()); + EXPECT_FALSE(pairing_failure_latch.Await(kWaitTimeout).result()); +} + TEST_F(PairerBrokerImplTest, FaileToWriteAccountkeyToRemoteDevice) { LogInAccount(); + auto repository = std::make_unique(); + repository->SetResultOfIsDeviceSavedToAccount( + absl::NotFoundError("not found")); ConfigurePairingContext(); SetPairingResult(std::nullopt); CreateMockDevice(DeviceFastPairVersion::kHigherThanV1, @@ -656,6 +695,8 @@ TEST_F(PairerBrokerImplTest, FaileToWriteAccountkeyToRemoteDevice) { TEST_F(PairerBrokerImplTest, FaileToWriteAccountkeyToFootprints) { LogInAccount(); auto repository = std::make_unique(); + repository->SetResultOfIsDeviceSavedToAccount( + absl::NotFoundError("not found")); repository->SetResultOfWriteAccountAssociationToFootprints( absl::InternalError("Failed to write account key to foot prints")); ConfigurePairingContext(); diff --git a/fastpair/repository/fake_fast_pair_repository.cc b/fastpair/repository/fake_fast_pair_repository.cc index 31da9be8..4c0491d7 100644 --- a/fastpair/repository/fake_fast_pair_repository.cc +++ b/fastpair/repository/fake_fast_pair_repository.cc @@ -56,6 +56,11 @@ void FakeFastPairRepository::SetResultOfDeleteAssociatedDeviceByAccountKey( deleted_associated_device_ = status; } +void FakeFastPairRepository::SetResultOfIsDeviceSavedToAccount( + absl::Status status) { + is_device_saved_to_account_ = status; +} + void FakeFastPairRepository::GetDeviceMetadata( absl::string_view hex_model_id, DeviceMetadataCallback callback) { executor_.Execute([this, callback = std::move(callback), @@ -69,14 +74,14 @@ void FakeFastPairRepository::GetDeviceMetadata( } void FakeFastPairRepository::WriteAccountAssociationToFootprints( - FastPairDevice& device, OperationToFootprintsCallback callback) { + FastPairDevice& device, OperationCallback callback) { executor_.Execute([callback = std::move(callback), this]() mutable { callback(write_account_association_to_footprints_); }); } void FakeFastPairRepository::DeleteAssociatedDeviceByAccountKey( - const AccountKey& account_key, OperationToFootprintsCallback callback) { + const AccountKey& account_key, OperationCallback callback) { executor_.Execute([callback = std::move(callback), this]() mutable { callback(deleted_associated_device_); }); @@ -89,6 +94,13 @@ void FakeFastPairRepository::CheckIfAssociatedWithCurrentAccount( }); } +void FakeFastPairRepository::IsDeviceSavedToAccount( + absl::string_view mac_address, OperationCallback callback) { + executor_.Execute([callback = std::move(callback), this]() mutable { + callback(is_device_saved_to_account_); + }); +} + std::unique_ptr FakeFastPairRepository::Create( absl::string_view model_id, absl::string_view public_anti_spoof_key) { proto::Device metadata; diff --git a/fastpair/repository/fake_fast_pair_repository.h b/fastpair/repository/fake_fast_pair_repository.h index a1ca8428..5afb3d24 100644 --- a/fastpair/repository/fake_fast_pair_repository.h +++ b/fastpair/repository/fake_fast_pair_repository.h @@ -46,6 +46,7 @@ class FakeFastPairRepository : public FastPairRepository { void SetResultOfCheckIfAssociatedWithCurrentAccount( std::optional account_key, std::optional model_id); + void SetResultOfIsDeviceSavedToAccount(absl::Status status); // FastPairRepository:: void AddObserver(Observer* observer) override{}; @@ -56,17 +57,19 @@ class FakeFastPairRepository : public FastPairRepository { void GetUserSavedDevices() override{}; - void WriteAccountAssociationToFootprints( - FastPairDevice& device, OperationToFootprintsCallback callback) override; + void WriteAccountAssociationToFootprints(FastPairDevice& device, + OperationCallback callback) override; - void DeleteAssociatedDeviceByAccountKey( - const AccountKey& account_key, - OperationToFootprintsCallback callback) override; + void DeleteAssociatedDeviceByAccountKey(const AccountKey& account_key, + OperationCallback callback) override; void CheckIfAssociatedWithCurrentAccount( AccountKeyFilter& account_key_filter, CheckAccountKeysCallback callback) override; + void IsDeviceSavedToAccount(absl::string_view mac_address, + OperationCallback callback) override; + private: absl::flat_hash_map> data_; @@ -77,6 +80,8 @@ class FakeFastPairRepository : public FastPairRepository { absl::Status write_account_association_to_footprints_; // Results of DeleteAssociatedDeviceByAccountKey absl::Status deleted_associated_device_; + // Results of IsDeviceSavedToAccount + absl::Status is_device_saved_to_account_; SingleThreadExecutor executor_; }; } // namespace fastpair diff --git a/fastpair/repository/fast_pair_repository.h b/fastpair/repository/fast_pair_repository.h index 7134c0fe..1f1ea902 100644 --- a/fastpair/repository/fast_pair_repository.h +++ b/fastpair/repository/fast_pair_repository.h @@ -36,8 +36,7 @@ using DeviceMetadataCallback = using CheckAccountKeysCallback = absl::AnyInvocable account_key, std::optional model_id)>; -using OperationToFootprintsCallback = - absl::AnyInvocable; +using OperationCallback = absl::AnyInvocable; class FastPairRepository { public: @@ -72,12 +71,11 @@ class FastPairRepository { // Stores the given |account_key| for a |device| on the Footprints server. virtual void WriteAccountAssociationToFootprints( - FastPairDevice& device, OperationToFootprintsCallback callback) = 0; + FastPairDevice& device, OperationCallback callback) = 0; // Deletes the associated data for a given |account_key|. virtual void DeleteAssociatedDeviceByAccountKey( - const AccountKey& account_key, - OperationToFootprintsCallback callback) = 0; + const AccountKey& account_key, OperationCallback callback) = 0; // Checks all account keys associated with current user's account against the // given filter. If a match is found, return the account key. @@ -85,6 +83,12 @@ class FastPairRepository { AccountKeyFilter& account_key_filter, CheckAccountKeysCallback callback) = 0; + // Checks if a device with an address |mac_address| is already saved to + // the user's account by cross referencing the |mac_address| with any + // associated account keys. + virtual void IsDeviceSavedToAccount(absl::string_view mac_address, + OperationCallback callback) = 0; + protected: static void SetInstance(FastPairRepository* instance); }; diff --git a/fastpair/repository/fast_pair_repository_impl.cc b/fastpair/repository/fast_pair_repository_impl.cc index 2f51044b..8dea41cd 100644 --- a/fastpair/repository/fast_pair_repository_impl.cc +++ b/fastpair/repository/fast_pair_repository_impl.cc @@ -56,6 +56,20 @@ bool DoesDeviceHaveForgetPattern(const proto::FastPairDevice& device) { return (device.sha256_account_key_public_address().compare( 0, kForgetPattern.length(), kForgetPattern) == 0); } + +// Checks if the mac address of a FastPairDevice is the same as the given +// |mac_address| by checking if the SHA256 from the given |device| equals to +// SHA256(concat(account_key of |device|, |mac_address|)). +bool IsDeviceSha256Matched(const proto::FastPairDevice& device, + absl::string_view mac_address) { + if (DoesDeviceHaveForgetPattern(device)) { + return false; + } + + return device.sha256_account_key_public_address() == + FastPairRepository::GenerateSha256OfAccountKeyAndMacAddress( + AccountKey(device.account_key()), mac_address); +} } // namespace FastPairRepositoryImpl::FastPairRepositoryImpl(FastPairClient* fast_pair_client) @@ -98,7 +112,7 @@ void FastPairRepositoryImpl::GetDeviceMetadata( } void FastPairRepositoryImpl::WriteAccountAssociationToFootprints( - FastPairDevice& device, OperationToFootprintsCallback callback) { + FastPairDevice& device, OperationCallback callback) { proto::UserWriteDeviceRequest request; auto* fast_pair_info = request.mutable_fast_pair_info(); BuildFastPairInfo(fast_pair_info, device); @@ -124,7 +138,7 @@ void FastPairRepositoryImpl::WriteAccountAssociationToFootprints( } void FastPairRepositoryImpl::DeleteAssociatedDeviceByAccountKey( - const AccountKey& account_key, OperationToFootprintsCallback callback) { + const AccountKey& account_key, OperationCallback callback) { std::string hex_string = absl::BytesToHexString(account_key.GetAsBytes()); absl::AsciiStrToUpper(&hex_string); executor_.Execute( @@ -230,5 +244,38 @@ void FastPairRepositoryImpl::CheckIfAssociatedWithCurrentAccount( }); } +void FastPairRepositoryImpl::IsDeviceSavedToAccount( + absl::string_view mac_address, OperationCallback callback) { + executor_.Execute( + "Check is device saved to account.", + [this, mac_address = std::string(mac_address), + callback = std::move(callback)]() mutable { + NEARBY_LOGS(INFO) << __func__ + << ": Start to check is device saved to account."; + proto::UserReadDevicesRequest request; + absl::StatusOr response = + fast_pair_client_->UserReadDevices(request); + if (!response.ok()) { + NEARBY_LOGS(WARNING) + << __func__ + << "Failed to get UserDeleteDeviceResponse from backend."; + std::move(callback)(response.status()); + return; + } + for (const auto& info : response->fast_pair_info()) { + if (info.has_device() && + IsDeviceSha256Matched(info.device(), mac_address)) { + NEARBY_LOGS(VERBOSE) + << __func__ << ": found a SHA256 match for device at address = " + << mac_address; + std::move(callback)(absl::OkStatus()); + return; + } + } + std::move(callback)(absl::NotFoundError("Device " + mac_address + + " is not saved to account.")); + }); +} + } // namespace fastpair } // namespace nearby diff --git a/fastpair/repository/fast_pair_repository_impl.h b/fastpair/repository/fast_pair_repository_impl.h index 74fb4abc..6e9327c5 100644 --- a/fastpair/repository/fast_pair_repository_impl.h +++ b/fastpair/repository/fast_pair_repository_impl.h @@ -44,17 +44,19 @@ class FastPairRepositoryImpl : public FastPairRepository { void GetUserSavedDevices() override; - void WriteAccountAssociationToFootprints( - FastPairDevice& device, OperationToFootprintsCallback callback) override; + void WriteAccountAssociationToFootprints(FastPairDevice& device, + OperationCallback callback) override; - void DeleteAssociatedDeviceByAccountKey( - const AccountKey& account_key, - OperationToFootprintsCallback callback) override; + void DeleteAssociatedDeviceByAccountKey(const AccountKey& account_key, + OperationCallback callback) override; void CheckIfAssociatedWithCurrentAccount( AccountKeyFilter& account_key_filter, CheckAccountKeysCallback callback) override; + void IsDeviceSavedToAccount(absl::string_view mac_address, + OperationCallback callback) override; + private: // A thread for running blocking tasks. SingleThreadExecutor executor_; diff --git a/fastpair/repository/fast_pair_repository_impl_test.cc b/fastpair/repository/fast_pair_repository_impl_test.cc index a40f4162..a0374410 100644 --- a/fastpair/repository/fast_pair_repository_impl_test.cc +++ b/fastpair/repository/fast_pair_repository_impl_test.cc @@ -468,6 +468,81 @@ TEST(FastPairRepositoryImplTest, DeviceNotAssociatedWithCurrentAccount) { }); latch.Await(); } + +TEST(FastPairRepositoryImplTest, DeviceIsSavedToCurrentAccount) { + FakeFastPairClient fake_fast_pair_client; + auto fast_pair_repository = + std::make_unique(&fake_fast_pair_client); + + // Sets up two devices to proto::UserReadDevicesResponse. + proto::UserReadDevicesResponse response_proto; + // Device 1 + FastPairDevice device_1(kHexModelId, kBleAddress, + Protocol::kFastPairInitialPairing); + AccountKey account_key_1(absl::HexStringToBytes(kAccountKey)); + device_1.SetAccountKey(account_key_1); + device_1.SetPublicAddress(kPublicAddress); + proto::GetObservedDeviceResponse get_observed_device_response; + DeviceMetadata device_metadata_1(get_observed_device_response); + device_1.SetMetadata(device_metadata_1); + auto* fast_pair_info_1 = response_proto.add_fast_pair_info(); + BuildFastPairInfo(fast_pair_info_1, device_1); + // Device 2 + auto* fast_pair_info_2 = response_proto.add_fast_pair_info(); + fast_pair_info_2->set_opt_in_status( + proto::OptInStatus::OPT_IN_STATUS_OPTED_IN); + fake_fast_pair_client.SetUserReadDevicesResponse(response_proto); + + CountDownLatch latch(1); + fast_pair_repository->IsDeviceSavedToAccount(kPublicAddress, + [&](absl::Status status) { + EXPECT_OK(status); + latch.CountDown(); + }); + latch.Await(); +} + +TEST(FastPairRepositoryImplTest, DeviceIsNotSavedToCurrentAccount) { + FakeFastPairClient fake_fast_pair_client; + auto fast_pair_repository = + std::make_unique(&fake_fast_pair_client); + + // Sets up two devices to proto::UserReadDevicesResponse. + proto::UserReadDevicesResponse response_proto; + FastPairDevice device(kHexModelId, kBleAddress, + Protocol::kFastPairInitialPairing); + AccountKey account_key(absl::HexStringToBytes(kAccountKey)); + device.SetAccountKey(account_key); + device.SetPublicAddress(kPublicAddress); + proto::GetObservedDeviceResponse get_observed_device_response; + DeviceMetadata device_metadata(get_observed_device_response); + device.SetMetadata(device_metadata); + auto* fast_pair_info = response_proto.add_fast_pair_info(); + BuildFastPairInfo(fast_pair_info, device); + fake_fast_pair_client.SetUserReadDevicesResponse(response_proto); + + CountDownLatch latch(1); + fast_pair_repository->IsDeviceSavedToAccount( + "11:22:33:44:55:66", [&](absl::Status status) { + EXPECT_EQ(status.code(), absl::StatusCode::kNotFound); + latch.CountDown(); + }); + latch.Await(); +} + +TEST(FastPairRepositoryImplTest, FailedToCheckDeviceIsSavedToCurrentAccount) { + FakeFastPairClient fake_fast_pair_client; + auto fast_pair_repository = + std::make_unique(&fake_fast_pair_client); + + CountDownLatch latch(1); + fast_pair_repository->IsDeviceSavedToAccount(kPublicAddress, + [&](absl::Status status) { + EXPECT_FALSE(status.ok()); + latch.CountDown(); + }); + latch.Await(); +} } // namespace } // namespace fastpair } // namespace nearby From c9d1b69d415e34fdca8d19c55e972355ebf81939 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Tue, 25 Jul 2023 18:00:39 -0700 Subject: [PATCH 012/128] Set expected latch before sending messages This closes a time window where the sent message was received before the latch was configured. PiperOrigin-RevId: 551050923 --- .../implementation/offline_service_controller_test.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/connections/implementation/offline_service_controller_test.cc b/connections/implementation/offline_service_controller_test.cc index 3cedc81c..43daafea 100644 --- a/connections/implementation/offline_service_controller_test.cc +++ b/connections/implementation/offline_service_controller_test.cc @@ -284,10 +284,10 @@ TEST_P(OfflineServiceControllerTest, CanSendBytePayload) { env_.Start(); OfflineSimulationUser user_a(kDeviceA, GetParam()); OfflineSimulationUser user_b(kDeviceB, GetParam()); + user_b.ExpectPayload(payload_latch_); ASSERT_TRUE(SetupConnection(user_a, user_b)); ByteArray message(std::string{kMessage}); user_a.SendPayload(Payload(message)); - user_b.ExpectPayload(payload_latch_); EXPECT_TRUE(payload_latch_.Await(kLongTimeout)); EXPECT_EQ(user_b.GetPayload().AsBytes(), message); user_a.Stop(); @@ -299,6 +299,7 @@ TEST_P(OfflineServiceControllerTest, CanSendStreamPayload) { env_.Start(); OfflineSimulationUser user_a(kDeviceA, GetParam()); OfflineSimulationUser user_b(kDeviceB, GetParam()); + user_b.ExpectPayload(payload_latch_); ASSERT_TRUE(SetupConnection(user_a, user_b)); ByteArray message(std::string{kMessage}); auto pipe = std::make_shared(); @@ -306,7 +307,6 @@ TEST_P(OfflineServiceControllerTest, CanSendStreamPayload) { user_a.SendPayload(Payload([pipe]() -> InputStream& { return pipe->GetInputStream(); // NOLINT })); - user_b.ExpectPayload(payload_latch_); tx.Write(message); EXPECT_TRUE(payload_latch_.Await(kLongTimeout)); ASSERT_NE(user_b.GetPayload().AsStream(), nullptr); @@ -326,6 +326,7 @@ TEST_P(OfflineServiceControllerTest, CanCancelStreamPayload) { env_.Start(); OfflineSimulationUser user_a(kDeviceA, GetParam()); OfflineSimulationUser user_b(kDeviceB, GetParam()); + user_b.ExpectPayload(payload_latch_); ASSERT_TRUE(SetupConnection(user_a, user_b)); ByteArray message(std::string{kMessage}); auto pipe = std::make_shared(); @@ -333,7 +334,6 @@ TEST_P(OfflineServiceControllerTest, CanCancelStreamPayload) { user_a.SendPayload(Payload([pipe]() -> InputStream& { return pipe->GetInputStream(); // NOLINT })); - user_b.ExpectPayload(payload_latch_); tx.Write(message); EXPECT_TRUE(payload_latch_.Await(kLongTimeout)); ASSERT_NE(user_b.GetPayload().AsStream(), nullptr); From 9c6b60df3ffb5407d59dda0c52c5b63edf4082bb Mon Sep 17 00:00:00 2001 From: Anay Wadhera Date: Tue, 25 Jul 2023 18:01:50 -0700 Subject: [PATCH 013/128] introduce v3 advertising options PiperOrigin-RevId: 551051166 --- connections/v3/BUILD | 1 + connections/v3/advertising_options.h | 51 ++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 connections/v3/advertising_options.h diff --git a/connections/v3/BUILD b/connections/v3/BUILD index 00b143d9..ef0fb1b7 100644 --- a/connections/v3/BUILD +++ b/connections/v3/BUILD @@ -4,6 +4,7 @@ cc_library( "connections_device.cc", ], hdrs = [ + "advertising_options.h", "bandwidth_info.h", "connection_listening_options.h", "connection_result.h", diff --git a/connections/v3/advertising_options.h b/connections/v3/advertising_options.h new file mode 100644 index 00000000..4e22c552 --- /dev/null +++ b/connections/v3/advertising_options.h @@ -0,0 +1,51 @@ +// 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_CONNECTIONS_V3_ADVERTISING_OPTIONS_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_V3_ADVERTISING_OPTIONS_H_ + +#include + +#include "connections/medium_selector.h" +#include "connections/power_level.h" +#include "connections/strategy.h" + +namespace nearby { +namespace connections { +namespace v3 { + +struct AdvertisingOptions { + Strategy strategy; + PowerLevel power_level = PowerLevel::kHighPower; + // If the device should listen to Bluetooth connections during BLE advertising + bool enable_bluetooth_listening = true; + // Allow Nearby Connections to toggle the Bluetooth radio before starting + // advertising. + bool allow_bluetooth_radio_toggling = true; + // Allow Nearby Connections to toggle the WiFi radio before starting + // advertising + bool allow_wifi_radio_toggling = true; + // If Nearby Connections should auto-upgrade bandwidth. + bool auto_upgrade_bandwidth = true; + bool enforce_topology_constraints = true; + std::string fast_advertisement_service_uuid; + BooleanMediumSelector advertising_mediums; + BooleanMediumSelector upgrade_mediums; +}; + +} // namespace v3 +} // namespace connections +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_V3_ADVERTISING_OPTIONS_H_ From 8cfbc884d7b3639842d847fb181f646bba2c01c1 Mon Sep 17 00:00:00 2001 From: Anay Wadhera Date: Tue, 25 Jul 2023 18:14:23 -0700 Subject: [PATCH 014/128] introduce v3 discovery options PiperOrigin-RevId: 551053578 --- connections/v3/BUILD | 1 + connections/v3/discovery_options.h | 45 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 connections/v3/discovery_options.h diff --git a/connections/v3/BUILD b/connections/v3/BUILD index ef0fb1b7..9b6d02e6 100644 --- a/connections/v3/BUILD +++ b/connections/v3/BUILD @@ -10,6 +10,7 @@ cc_library( "connection_result.h", "connections_device.h", "connections_device_provider.h", + "discovery_options.h", "listeners.h", "listening_result.h", "params.h", diff --git a/connections/v3/discovery_options.h b/connections/v3/discovery_options.h new file mode 100644 index 00000000..d55021da --- /dev/null +++ b/connections/v3/discovery_options.h @@ -0,0 +1,45 @@ +// 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_CONNECTIONS_V3_DISCOVERY_OPTIONS_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_V3_DISCOVERY_OPTIONS_H_ + +#include + +#include "connections/medium_selector.h" +#include "connections/power_level.h" +#include "connections/strategy.h" + +namespace nearby { +namespace connections { +namespace v3 { + +struct DiscoveryOptions { + // Must match advertising strategy to see advertisements. + Strategy strategy; + PowerLevel power_level = PowerLevel::kHighPower; + // Allow Nearby Connections to toggle on Bluetooth radio before starting + // discovery. + bool allow_bluetooth_radio_toggling = true; + // Allow Nearby Connections to toggle on WiFi radio before starting discovery. + bool allow_wifi_radio_toggling = true; + std::string fast_advertisement_service_uuid; + BooleanMediumSelector discovery_mediums; +}; + +} // namespace v3 +} // namespace connections +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_V3_DISCOVERY_OPTIONS_H_ From de3f638129e4eb922a6af48419e0060b9d30f26a Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Tue, 25 Jul 2023 20:22:49 -0700 Subject: [PATCH 015/128] Extract BLE code into its own target PiperOrigin-RevId: 551074383 --- internal/platform/implementation/apple/BUILD | 34 +++++++++++++++---- .../platform/implementation/apple/Tests/BUILD | 1 + 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 7563076c..a43eb67c 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -23,9 +23,7 @@ package(default_visibility = [ objc_library( name = "apple", srcs = [ - "ble.mm", "ble_utils.mm", - "bluetooth_adapter.mm", "crypto.mm", "device_info.mm", "log_message.mm", @@ -38,9 +36,7 @@ objc_library( "wifi_lan.mm", ], hdrs = [ - "ble.h", "ble_utils.h", - "bluetooth_adapter.h", "device_info.h", "log_message.h", "multi_thread_executor.h", @@ -57,8 +53,8 @@ objc_library( deps = [ ":Platform_cc", ":Shared", + ":ble", "//internal/platform:base", - "//internal/platform:cancellation_flag", "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", "//internal/platform/implementation:types", @@ -73,7 +69,6 @@ objc_library( "@com_google_absl//absl/strings", "@com_google_absl//absl/synchronization", "@com_google_absl//absl/time", - "@com_google_absl//absl/types:optional", "@com_google_absl//absl/types:span", "@nlohmann_json//:json", ] + select({ @@ -84,6 +79,33 @@ objc_library( }), ) +objc_library( + name = "ble", + srcs = [ + "ble.mm", + "bluetooth_adapter.mm", + "utils.mm", + ], + hdrs = [ + "ble.h", + "bluetooth_adapter.h", + "utils.h", + ], + # Prevent Objective-C++ headers from being pulled into swift. + aspect_hints = ["//tools/build_defs/swift:no_module"], + deps = [ + "//internal/platform:base", + "//internal/platform:cancellation_flag", + "//internal/platform/implementation:comm", + "//internal/platform/implementation/apple/Mediums", + "//third_party/apple_frameworks:CoreBluetooth", + "//third_party/apple_frameworks:Foundation", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", + ], +) + objc_library( name = "Shared", srcs = [ diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index 8f4ee847..a14ce29d 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -45,6 +45,7 @@ objc_library( "//internal/platform/implementation:platform", "//internal/platform/implementation:types", "//internal/platform/implementation/apple", + "//internal/platform/implementation/apple:ble", "//internal/platform/implementation/apple/Mediums", "//third_party/apple_frameworks:CoreBluetooth", "//third_party/apple_frameworks:Foundation", From 4958a32c355eb6d5d7641d96b38cb5e9c389da4c Mon Sep 17 00:00:00 2001 From: Anay Wadhera Date: Tue, 25 Jul 2023 21:06:40 -0700 Subject: [PATCH 016/128] make ResultCallback a bare function type and convert to AnyInvocable PiperOrigin-RevId: 551081080 --- connections/c/core_adapter.cc | 28 +- connections/c/listeners_w.cc | 15 +- connections/c/listeners_w.h | 19 +- connections/core.cc | 74 +-- connections/core_test.cc | 62 +- .../mock_service_controller_router.h | 49 +- .../service_controller_router.cc | 259 ++++---- .../service_controller_router.h | 51 +- .../service_controller_router_test.cc | 594 ++++++++++++++---- connections/listeners.h | 9 +- .../Sources/GNCCoreAdapter.mm | 62 +- 11 files changed, 782 insertions(+), 440 deletions(-) diff --git a/connections/c/core_adapter.cc b/connections/c/core_adapter.cc index fa038f3a..f8521f6d 100644 --- a/connections/c/core_adapter.cc +++ b/connections/c/core_adapter.cc @@ -32,8 +32,7 @@ void CloseCore(Core *pCore) { if (pCore == nullptr) { return; } - pCore->StopAllEndpoints( - {.result_cb = std::function{[](Status) {}}}); + pCore->StopAllEndpoints([](Status) {}); delete pCore; } @@ -80,14 +79,14 @@ void StartAdvertising(Core *pCore, const char *service_id, advertising_options.strategy = connections::Strategy::kP2pStar; pCore->StartAdvertising(service_id, advertising_options, crInfo, - *callback.GetImpl()); + std::move(*callback.GetImpl())); } void StopAdvertising(connections::Core *pCore, ResultCallbackW callback) { if (pCore == nullptr) { return; } - pCore->StopAdvertising(*callback.GetImpl()); + pCore->StopAdvertising(std::move(*callback.GetImpl())); } void StartDiscovery(connections::Core *pCore, const char *service_id, @@ -134,7 +133,7 @@ void StopDiscovery(connections::Core *pCore, ResultCallbackW callback) { if (pCore == nullptr) { return; } - pCore->StopDiscovery(*callback.GetImpl()); + pCore->StopDiscovery(std::move(*callback.GetImpl())); } void InjectEndpoint(connections::Core *pCore, char *service_id, @@ -153,7 +152,7 @@ void InjectEndpoint(connections::Core *pCore, char *service_id, metadata.remote_bluetooth_mac_address_size}; pCore->InjectEndpoint(service_id, outOfBandConnectionMetadata, - *callback.GetImpl()); + std::move(*callback.GetImpl())); } void RequestConnection(connections::Core *pCore, const char *endpoint_id, @@ -203,7 +202,7 @@ void RequestConnection(connections::Core *pCore, const char *endpoint_id, connection_options.strategy = connections::Strategy::kP2pStar; pCore->RequestConnection(endpoint_id, connectionRequestInfo, - connection_options, *callback.GetImpl()); + connection_options, std::move(*callback.GetImpl())); } void AcceptConnection(connections::Core *pCore, const char *endpoint_id, @@ -214,7 +213,7 @@ void AcceptConnection(connections::Core *pCore, const char *endpoint_id, connections::PayloadListener payload_listener = std::move(*listener.GetImpl()); pCore->AcceptConnection(endpoint_id, std::move(payload_listener), - *callback.GetImpl()); + std::move(*callback.GetImpl())); } void RejectConnection(connections::Core *pCore, const char *endpoint_id, @@ -222,7 +221,7 @@ void RejectConnection(connections::Core *pCore, const char *endpoint_id, if (pCore == nullptr) { return; } - pCore->RejectConnection(endpoint_id, *callback.GetImpl()); + pCore->RejectConnection(endpoint_id, std::move(*callback.GetImpl())); } void SendPayload(connections::Core *pCore, @@ -235,7 +234,8 @@ void SendPayload(connections::Core *pCore, } std::string payloadData = std::string(*endpoint_ids); absl::Span span{&payloadData, 1}; - pCore->SendPayload(span, std::move(*payloadw.GetImpl()), *callback.GetImpl()); + pCore->SendPayload(span, std::move(*payloadw.GetImpl()), + std::move(*callback.GetImpl())); } void CancelPayload(connections::Core *pCore, std::int64_t payload_id, @@ -243,7 +243,7 @@ void CancelPayload(connections::Core *pCore, std::int64_t payload_id, if (pCore == nullptr) { return; } - pCore->CancelPayload(payload_id, *callback.GetImpl()); + pCore->CancelPayload(payload_id, std::move(*callback.GetImpl())); } void DisconnectFromEndpoint(connections::Core *pCore, const char *endpoint_id, @@ -251,14 +251,14 @@ void DisconnectFromEndpoint(connections::Core *pCore, const char *endpoint_id, if (pCore == nullptr) { return; } - pCore->DisconnectFromEndpoint(endpoint_id, *callback.GetImpl()); + pCore->DisconnectFromEndpoint(endpoint_id, std::move(*callback.GetImpl())); } void StopAllEndpoints(connections::Core *pCore, ResultCallbackW callback) { if (pCore == nullptr) { return; } - pCore->StopAllEndpoints(*callback.GetImpl()); + pCore->StopAllEndpoints(std::move(*callback.GetImpl())); } void InitiateBandwidthUpgrade(connections::Core *pCore, char *endpoint_id, @@ -266,7 +266,7 @@ void InitiateBandwidthUpgrade(connections::Core *pCore, char *endpoint_id, if (pCore == nullptr) { return; } - pCore->InitiateBandwidthUpgrade(endpoint_id, *callback.GetImpl()); + pCore->InitiateBandwidthUpgrade(endpoint_id, std::move(*callback.GetImpl())); } const char *GetLocalEndpointId(connections::Core *pCore) { diff --git a/connections/c/listeners_w.cc b/connections/c/listeners_w.cc index e99e22fe..cfed3fac 100644 --- a/connections/c/listeners_w.cc +++ b/connections/c/listeners_w.cc @@ -12,6 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include + #include "connections/c/listeners_w.h" #include "connections/listeners.h" @@ -20,9 +23,6 @@ namespace nearby { // Must implement Deleters, since the connections classes weren't // fully defined in the header namespace connections { -void ResultCallbackDeleter::operator()(connections::ResultCallback *p) { - delete p; -} void ConnectionListenerDeleter::operator()(connections::ConnectionListener *p) { delete p; } @@ -41,21 +41,18 @@ static ResultCallbackW *ResultCallbackImpl; void ResultCB(Status status) { ResultCallbackImpl->result_cb(status); } ResultCallbackW::ResultCallbackW() - : impl_(std::unique_ptr( - new connections::ResultCallback())) { + : impl(std::make_unique(ResultCB)) { ResultCallbackImpl = this; - impl_->result_cb = ResultCB; } ResultCallbackW::~ResultCallbackW() {} ResultCallbackW::ResultCallbackW(ResultCallbackW &other) { - impl_ = std::move(other.impl_); + impl = std::move(other.impl); } ResultCallbackW::ResultCallbackW(ResultCallbackW &&other) noexcept { - impl_ = std::move(other.impl_); + impl = std::move(other.impl); } ConnectionListenerW::ConnectionListenerW(InitiatedCB initiatedCB, diff --git a/connections/c/listeners_w.h b/connections/c/listeners_w.h index a0b49b28..5d69acfd 100644 --- a/connections/c/listeners_w.h +++ b/connections/c/listeners_w.h @@ -15,10 +15,8 @@ #ifndef THIRD_PARTY_NEARBY_CONNECTIONS_C_LISTENERS_W_H_ #define THIRD_PARTY_NEARBY_CONNECTIONS_C_LISTENERS_W_H_ -#include -#include #include -#include +#include // This file defines all the protocol listeners and their parameter structures. // Listeners are defined as collections of std::function instances, which is @@ -50,10 +48,7 @@ struct DLL_API PayloadListenerDeleter { void operator()(connections::PayloadListener* p); }; -struct ResultCallback; -struct ResultCallbackDeleter { - void operator()(connections::ResultCallback* p); -}; +using ResultCallback = absl::AnyInvocable; struct ConnectionResponseInfo; struct PayloadProgressInfo; @@ -92,16 +87,12 @@ struct DLL_API ResultCallbackW { void (*result_cb)(Status status) = DefaultConstructor; - std::unique_ptr - GetImpl() { - return std::move(impl_); + std::unique_ptr GetImpl() { + return std::move(impl); } private: - std::unique_ptr - impl_; + std::unique_ptr impl; }; struct DLL_API ConnectionResponseInfoW { diff --git a/connections/core.cc b/connections/core.cc index 27729a85..00a50701 100644 --- a/connections/core.cc +++ b/connections/core.cc @@ -54,10 +54,7 @@ Core::Core(ServiceControllerRouter* router) : router_(router) {} Core::~Core() { CountDownLatch latch(1); - router_->StopAllEndpoints( - &client_, { - .result_cb = [&latch](Status) { latch.CountDown(); }, - }); + router_->StopAllEndpoints(&client_, [&latch](Status) { latch.CountDown(); }); if (!latch.Await(kWaitForDisconnect).result()) { NEARBY_LOG(FATAL, "Unable to shutdown"); } @@ -75,11 +72,11 @@ void Core::StartAdvertising(absl::string_view service_id, CHECK(advertising_options.strategy.IsValid()); router_->StartAdvertising(&client_, service_id, advertising_options, info, - callback); + std::move(callback)); } -void Core::StopAdvertising(const ResultCallback callback) { - router_->StopAdvertising(&client_, callback); +void Core::StopAdvertising(ResultCallback callback) { + router_->StopAdvertising(&client_, std::move(callback)); } void Core::StartDiscovery(absl::string_view service_id, @@ -89,18 +86,18 @@ void Core::StartDiscovery(absl::string_view service_id, CHECK(discovery_options.strategy.IsValid()); router_->StartDiscovery(&client_, service_id, discovery_options, listener, - callback); + std::move(callback)); } void Core::InjectEndpoint(absl::string_view service_id, OutOfBandConnectionMetadata metadata, ResultCallback callback) { CheckServiceId(service_id); - router_->InjectEndpoint(&client_, service_id, metadata, callback); + router_->InjectEndpoint(&client_, service_id, metadata, std::move(callback)); } void Core::StopDiscovery(ResultCallback callback) { - router_->StopDiscovery(&client_, callback); + router_->StopDiscovery(&client_, std::move(callback)); } void Core::RequestConnection(absl::string_view endpoint_id, @@ -128,7 +125,7 @@ void Core::RequestConnection(absl::string_view endpoint_id, } router_->RequestConnection(&client_, endpoint_id, info, connection_options, - callback); + std::move(callback)); } void Core::AcceptConnection(absl::string_view endpoint_id, @@ -136,19 +133,19 @@ void Core::AcceptConnection(absl::string_view endpoint_id, CHECK(!endpoint_id.empty()); router_->AcceptConnection(&client_, endpoint_id, std::move(listener), - callback); + std::move(callback)); } void Core::RejectConnection(absl::string_view endpoint_id, ResultCallback callback) { CHECK(!endpoint_id.empty()); - router_->RejectConnection(&client_, endpoint_id, callback); + router_->RejectConnection(&client_, endpoint_id, std::move(callback)); } void Core::InitiateBandwidthUpgrade(absl::string_view endpoint_id, ResultCallback callback) { - router_->InitiateBandwidthUpgrade(&client_, endpoint_id, callback); + router_->InitiateBandwidthUpgrade(&client_, endpoint_id, std::move(callback)); } void Core::SendPayload(absl::Span endpoint_ids, @@ -156,28 +153,29 @@ void Core::SendPayload(absl::Span endpoint_ids, CHECK(payload.GetType() != PayloadType::kUnknown); CHECK(!endpoint_ids.empty()); - router_->SendPayload(&client_, endpoint_ids, std::move(payload), callback); + router_->SendPayload(&client_, endpoint_ids, std::move(payload), + std::move(callback)); } void Core::CancelPayload(std::int64_t payload_id, ResultCallback callback) { CHECK_NE(payload_id, 0); - router_->CancelPayload(&client_, payload_id, callback); + router_->CancelPayload(&client_, payload_id, std::move(callback)); } void Core::DisconnectFromEndpoint(absl::string_view endpoint_id, ResultCallback callback) { CHECK(!endpoint_id.empty()); - router_->DisconnectFromEndpoint(&client_, endpoint_id, callback); + router_->DisconnectFromEndpoint(&client_, endpoint_id, std::move(callback)); } void Core::StopAllEndpoints(ResultCallback callback) { - router_->StopAllEndpoints(&client_, callback); + router_->StopAllEndpoints(&client_, std::move(callback)); } void Core::SetCustomSavePath(absl::string_view path, ResultCallback callback) { - router_->SetCustomSavePath(&client_, path, callback); + router_->SetCustomSavePath(&client_, path, std::move(callback)); } std::string Core::Dump() { return client_.Dump(); } @@ -240,7 +238,8 @@ void Core::StartAdvertisingV3(absl::string_view service_id, .endpoint_info = local_endpoint_info, .listener = old_listener, }; - StartAdvertising(service_id, advertising_options, old_info, callback); + StartAdvertising(service_id, advertising_options, old_info, + std::move(callback)); } void Core::StartAdvertisingV3(absl::string_view service_id, @@ -300,11 +299,12 @@ void Core::StartAdvertisingV3(absl::string_view service_id, .endpoint_info = local_endpoint_info, .listener = old_listener, }; - StartAdvertising(service_id, advertising_options, old_info, callback); + StartAdvertising(service_id, advertising_options, old_info, + std::move(callback)); } void Core::StopAdvertisingV3(ResultCallback result_cb) { - StopAdvertising(result_cb); + StopAdvertising(std::move(result_cb)); } void Core::StartDiscoveryV3(absl::string_view service_id, @@ -332,11 +332,12 @@ void Core::StartDiscoveryV3(absl::string_view service_id, listener.endpoint_distance_changed_cb(remote, distance_info); }, }; - StartDiscovery(service_id, discovery_options, old_listener, callback); + StartDiscovery(service_id, discovery_options, old_listener, + std::move(callback)); } void Core::StopDiscoveryV3(ResultCallback result_cb) { - router_->StopDiscovery(&client_, result_cb); + router_->StopDiscovery(&client_, std::move(result_cb)); } void Core::StartListeningForIncomingConnectionsV3( @@ -383,7 +384,7 @@ void Core::RequestConnectionV3(const NearbyDevice& local_device, FeatureFlags::GetInstance().GetFlags().keep_alive_timeout_millis; } router_->RequestConnectionV3(&client_, remote_device, std::move(info), - connection_options, result_cb); + connection_options, std::move(result_cb)); } void Core::RequestConnectionV3(const NearbyDevice& remote_device, @@ -414,7 +415,7 @@ void Core::RequestConnectionV3(const NearbyDevice& remote_device, FeatureFlags::GetInstance().GetFlags().keep_alive_timeout_millis; } router_->RequestConnectionV3(&client_, remote_device, std::move(info), - connection_options, result_cb); + connection_options, std::move(result_cb)); } void Core::AcceptConnectionV3(const NearbyDevice& remote_device, @@ -423,14 +424,14 @@ void Core::AcceptConnectionV3(const NearbyDevice& remote_device, CHECK(!remote_device.GetEndpointId().empty()); router_->AcceptConnectionV3(&client_, remote_device, std::move(listener_cb), - result_cb); + std::move(result_cb)); } void Core::RejectConnectionV3(const NearbyDevice& remote_device, ResultCallback result_cb) { CHECK(!remote_device.GetEndpointId().empty()); - router_->RejectConnectionV3(&client_, remote_device, result_cb); + router_->RejectConnectionV3(&client_, remote_device, std::move(result_cb)); } void Core::SendPayloadV3(const NearbyDevice& remote_device, Payload payload, @@ -439,44 +440,47 @@ void Core::SendPayloadV3(const NearbyDevice& remote_device, Payload payload, CHECK(!remote_device.GetEndpointId().empty()); router_->SendPayloadV3(&client_, remote_device, std::move(payload), - result_cb); + std::move(result_cb)); } void Core::CancelPayloadV3(const NearbyDevice& remote_device, int64_t payload_id, ResultCallback result_cb) { CHECK_NE(payload_id, 0); - router_->CancelPayloadV3(&client_, remote_device, payload_id, result_cb); + router_->CancelPayloadV3(&client_, remote_device, payload_id, + std::move(result_cb)); } void Core::DisconnectFromDeviceV3(const NearbyDevice& remote_device, ResultCallback result_cb) { CHECK(!remote_device.GetEndpointId().empty()); - router_->DisconnectFromDeviceV3(&client_, remote_device, result_cb); + router_->DisconnectFromDeviceV3(&client_, remote_device, + std::move(result_cb)); } void Core::StopAllDevicesV3(ResultCallback result_cb) { - router_->StopAllEndpoints(&client_, result_cb); + router_->StopAllEndpoints(&client_, std::move(result_cb)); } void Core::InitiateBandwidthUpgradeV3(const NearbyDevice& remote_device, ResultCallback result_cb) { - router_->InitiateBandwidthUpgradeV3(&client_, remote_device, result_cb); + router_->InitiateBandwidthUpgradeV3(&client_, remote_device, + std::move(result_cb)); } void Core::UpdateAdvertisingOptionsV3(absl::string_view service_id, AdvertisingOptions advertising_options, ResultCallback result_cb) { router_->UpdateAdvertisingOptionsV3(&client_, service_id, advertising_options, - result_cb); + std::move(result_cb)); } void Core::UpdateDiscoveryOptionsV3(absl::string_view service_id, DiscoveryOptions discovery_options, ResultCallback result_cb) { router_->UpdateDiscoveryOptionsV3(&client_, service_id, discovery_options, - result_cb); + std::move(result_cb)); } } // namespace connections diff --git a/connections/core_test.cc b/connections/core_test.cc index 22ec1afa..cd5d75d1 100644 --- a/connections/core_test.cc +++ b/connections/core_test.cc @@ -54,8 +54,8 @@ TEST(CoreTest, ConstructorDestructorWorks) { MockServiceControllerRouter mock; // Called when Core is destroyed. EXPECT_CALL(mock, StopAllEndpoints) - .WillOnce([&](ClientProxy* client, const ResultCallback& callback) { - callback.result_cb({Status::kSuccess}); + .WillOnce([&](ClientProxy* client, ResultCallback callback) { + callback({Status::kSuccess}); }); Core core{&mock}; } @@ -71,6 +71,42 @@ TEST(CoreTest, DestructorReportsFatalFailure) { "Unable to shutdown"); } +TEST(CoreTest, RequestConnectionCallsScRouter) { + MockServiceControllerRouter mock; + // Called when Core is destroyed. + EXPECT_CALL(mock, StopAllEndpoints) + .WillOnce([&](ClientProxy* client, ResultCallback callback) { + callback({Status::kSuccess}); + }); + EXPECT_CALL(mock, RequestConnection); + Core core{&mock}; + core.RequestConnection("TEST", {}, {}, {}); +} + +TEST(CoreTest, AcceptConnectionCallsScRouter) { + MockServiceControllerRouter mock; + // Called when Core is destroyed. + EXPECT_CALL(mock, StopAllEndpoints) + .WillOnce([&](ClientProxy* client, ResultCallback callback) { + callback({Status::kSuccess}); + }); + EXPECT_CALL(mock, AcceptConnection); + Core core{&mock}; + core.AcceptConnection("TEST", {}, {}); +} + +TEST(CoreTest, SendPayloadCallsScRouter) { + MockServiceControllerRouter mock; + // Called when Core is destroyed. + EXPECT_CALL(mock, StopAllEndpoints) + .WillOnce([&](ClientProxy* client, ResultCallback callback) { + callback({Status::kSuccess}); + }); + EXPECT_CALL(mock, SendPayload); + Core core{&mock}; + core.SendPayload({"TEST"}, Payload(ByteArray("Hello world")), {}); +} + TEST(CoreV3Test, TestCallbackWrapWorksStartAdvertisingV3FourArgs) { MockServiceControllerRouter mock; EXPECT_CALL(mock, StartAdvertising) @@ -86,9 +122,9 @@ TEST(CoreV3Test, TestCallbackWrapWorksStartAdvertisingV3FourArgs) { info.listener.disconnected_cb("FAKE"); }); EXPECT_CALL(mock, StopAllEndpoints) - .WillOnce([&](ClientProxy* client, const ResultCallback& callback) { + .WillOnce([&](ClientProxy* client, ResultCallback callback) { NEARBY_LOGS(INFO) << "StopAllEndpoints called"; - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); Core core{&mock}; CountDownLatch result_latch(2); @@ -132,7 +168,7 @@ TEST(CoreV3Test, TestStartAdvertisingV3NonConnectionsDeviceProvider) { MockServiceControllerRouter mock; EXPECT_CALL(mock, StartAdvertising) .WillOnce([&](ClientProxy*, absl::string_view, const AdvertisingOptions&, - const ConnectionRequestInfo& info, const ResultCallback&) { + const ConnectionRequestInfo& info, ResultCallback) { NEARBY_LOGS(INFO) << "StartAdvertising called"; ASSERT_TRUE(info.endpoint_info.Empty()); // call all callbacks to make sure it all gets called correctly. @@ -143,9 +179,9 @@ TEST(CoreV3Test, TestStartAdvertisingV3NonConnectionsDeviceProvider) { info.listener.disconnected_cb("FAKE"); }); EXPECT_CALL(mock, StopAllEndpoints) - .WillOnce([&](ClientProxy* client, const ResultCallback& callback) { + .WillOnce([&](ClientProxy* client, ResultCallback callback) { NEARBY_LOGS(INFO) << "StopAllEndpoints called"; - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); Core core{&mock}; CountDownLatch result_latch(2); @@ -202,9 +238,9 @@ TEST(CoreV3Test, TestStartAdvertisingV3NonConnectionsDevice) { info.listener.disconnected_cb("FAKE"); }); EXPECT_CALL(mock, StopAllEndpoints) - .WillOnce([&](ClientProxy* client, const ResultCallback& callback) { + .WillOnce([&](ClientProxy* client, ResultCallback callback) { NEARBY_LOGS(INFO) << "StopAllEndpoints called"; - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); Core core{&mock}; CountDownLatch result_latch(2); @@ -260,9 +296,9 @@ TEST(CoreV3Test, TestCallbackWrapWorksStartAdvertisingV3FiveArgs) { info.listener.disconnected_cb("FAKE"); }); EXPECT_CALL(mock, StopAllEndpoints) - .WillOnce([&](ClientProxy* client, const ResultCallback& callback) { + .WillOnce([&](ClientProxy* client, ResultCallback callback) { NEARBY_LOGS(INFO) << "StopAllEndpoints called"; - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); Core core{&mock}; CountDownLatch result_latch(2); @@ -315,9 +351,9 @@ TEST(CoreV3Test, TestCallbackWrapWorksStartDiscoveryV3) { info.endpoint_lost_cb("FAKE"); }); EXPECT_CALL(mock, StopAllEndpoints) - .WillOnce([&](ClientProxy* client, const ResultCallback& callback) { + .WillOnce([&](ClientProxy* client, ResultCallback callback) { NEARBY_LOGS(INFO) << "StopAllEndpoints called"; - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); DiscoveryOptions options; options.strategy = Strategy::kP2pCluster; diff --git a/connections/implementation/mock_service_controller_router.h b/connections/implementation/mock_service_controller_router.h index 9133fd1a..4d8ea108 100644 --- a/connections/implementation/mock_service_controller_router.h +++ b/connections/implementation/mock_service_controller_router.h @@ -26,118 +26,113 @@ class MockServiceControllerRouter : public ServiceControllerRouter { MOCK_METHOD(void, StartAdvertising, (ClientProxy * client, absl::string_view service_id, const AdvertisingOptions& advertising_options, - const ConnectionRequestInfo& info, - const ResultCallback& callback), + const ConnectionRequestInfo& info, ResultCallback callback), (override)); MOCK_METHOD(void, StopAdvertising, - (ClientProxy * client, const ResultCallback& callback), - (override)); + (ClientProxy * client, ResultCallback callback), (override)); MOCK_METHOD(void, StartDiscovery, (ClientProxy * client, absl::string_view service_id, const DiscoveryOptions& discovery_options, - const DiscoveryListener& listener, - const ResultCallback& callback), + const DiscoveryListener& listener, ResultCallback callback), (override)); MOCK_METHOD(void, StopDiscovery, - (ClientProxy * client, const ResultCallback& callback), - (override)); + (ClientProxy * client, ResultCallback callback), (override)); MOCK_METHOD(void, InjectEndpoint, (ClientProxy * client, absl::string_view service_id, const OutOfBandConnectionMetadata& metadata, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, RequestConnection, (ClientProxy * client, absl::string_view endpoint_id, const ConnectionRequestInfo& info, const ConnectionOptions& connection_options, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, AcceptConnection, (ClientProxy * client, absl::string_view endpoint_id, - PayloadListener listener, const ResultCallback& callback), + PayloadListener listener, ResultCallback callback), (override)); MOCK_METHOD(void, RejectConnection, (ClientProxy * client, absl::string_view endpoint_id, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, InitiateBandwidthUpgrade, (ClientProxy * client, absl::string_view endpoint_id, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, SendPayload, (ClientProxy * client, absl::Span endpoint_ids, - Payload payload, const ResultCallback& callback), + Payload payload, ResultCallback callback), (override)); MOCK_METHOD(void, CancelPayload, (ClientProxy * client, std::uint64_t payload_id, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, DisconnectFromEndpoint, (ClientProxy * client, absl::string_view endpoint_id, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, StopAllEndpoints, - (ClientProxy * client, const ResultCallback& callback), - (override)); + (ClientProxy * client, ResultCallback callback), (override)); MOCK_METHOD(void, SetCustomSavePath, (ClientProxy * client, absl::string_view path, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, RequestConnectionV3, (ClientProxy * client, const NearbyDevice&, v3::ConnectionRequestInfo, const ConnectionOptions&, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, AcceptConnectionV3, (ClientProxy * client, const NearbyDevice&, v3::PayloadListener, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, RejectConnectionV3, (ClientProxy * client, const NearbyDevice&, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, InitiateBandwidthUpgradeV3, (ClientProxy * client, const NearbyDevice&, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, SendPayloadV3, (ClientProxy * client, const NearbyDevice&, Payload, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, DisconnectFromDeviceV3, (ClientProxy * client, const NearbyDevice&, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, UpdateAdvertisingOptionsV3, (ClientProxy * client, absl::string_view service_id, const AdvertisingOptions& advertising_options, - const ResultCallback& callback), + ResultCallback callback), (override)); MOCK_METHOD(void, UpdateDiscoveryOptionsV3, (ClientProxy * client, absl::string_view service_id, const DiscoveryOptions& discovery_options, - const ResultCallback& callback), + ResultCallback callback), (override)); }; diff --git a/connections/implementation/service_controller_router.cc b/connections/implementation/service_controller_router.cc index 5b2447b1..e50a3999 100644 --- a/connections/implementation/service_controller_router.cc +++ b/connections/implementation/service_controller_router.cc @@ -98,101 +98,103 @@ ServiceControllerRouter::~ServiceControllerRouter() { void ServiceControllerRouter::StartAdvertising( ClientProxy* client, absl::string_view service_id, const AdvertisingOptions& advertising_options, - const ConnectionRequestInfo& info, const ResultCallback& callback) { + const ConnectionRequestInfo& info, ResultCallback callback) { RouteToServiceController( "scr-start-advertising", [this, client, service_id = std::string(service_id), advertising_options, - info, callback]() { + info, callback = std::move(callback)]() mutable { if (client->IsAdvertising()) { - callback.result_cb({Status::kAlreadyAdvertising}); + callback({Status::kAlreadyAdvertising}); return; } - callback.result_cb(GetServiceController()->StartAdvertising( + callback(GetServiceController()->StartAdvertising( client, service_id, advertising_options, info)); }); } void ServiceControllerRouter::StopAdvertising(ClientProxy* client, - const ResultCallback& callback) { - RouteToServiceController("scr-stop-advertising", [this, client, callback]() { - if (client->IsAdvertising()) { - GetServiceController()->StopAdvertising(client); - } - callback.result_cb({Status::kSuccess}); - }); + ResultCallback callback) { + RouteToServiceController( + "scr-stop-advertising", + [this, client, callback = std::move(callback)]() mutable { + if (client->IsAdvertising()) { + GetServiceController()->StopAdvertising(client); + } + callback({Status::kSuccess}); + }); } void ServiceControllerRouter::StartDiscovery( ClientProxy* client, absl::string_view service_id, const DiscoveryOptions& discovery_options, - const DiscoveryListener& listener, const ResultCallback& callback) { + const DiscoveryListener& listener, ResultCallback callback) { RouteToServiceController( "scr-start-discovery", [this, client, service_id = std::string(service_id), discovery_options, - listener, callback]() { + listener, callback = std::move(callback)]() mutable { if (client->IsDiscovering()) { - callback.result_cb({Status::kAlreadyDiscovering}); + callback({Status::kAlreadyDiscovering}); return; } - callback.result_cb(GetServiceController()->StartDiscovery( + callback(GetServiceController()->StartDiscovery( client, service_id, discovery_options, listener)); }); } void ServiceControllerRouter::StopDiscovery(ClientProxy* client, - const ResultCallback& callback) { - RouteToServiceController("scr-stop-discovery", [this, client, callback]() { - if (client->IsDiscovering()) { - GetServiceController()->StopDiscovery(client); - } - callback.result_cb({Status::kSuccess}); - }); + ResultCallback callback) { + RouteToServiceController( + "scr-stop-discovery", + [this, client, callback = std::move(callback)]() mutable { + if (client->IsDiscovering()) { + GetServiceController()->StopDiscovery(client); + } + callback({Status::kSuccess}); + }); } void ServiceControllerRouter::InjectEndpoint( ClientProxy* client, absl::string_view service_id, - const OutOfBandConnectionMetadata& metadata, - const ResultCallback& callback) { + const OutOfBandConnectionMetadata& metadata, ResultCallback callback) { RouteToServiceController( "scr-inject-endpoint", [this, client, service_id = std::string(service_id), metadata, - callback]() { + callback = std::move(callback)]() mutable { // Currently, Bluetooth is the only supported medium for endpoint // injection. if (metadata.medium != Medium::BLUETOOTH || metadata.remote_bluetooth_mac_address.size() != kMacAddressLength) { - callback.result_cb({Status::kError}); + callback({Status::kError}); return; } if (metadata.endpoint_id.size() != kEndpointIdLength) { - callback.result_cb({Status::kError}); + callback({Status::kError}); return; } if (metadata.endpoint_info.Empty() || metadata.endpoint_info.size() > kMaxEndpointInfoLength) { - callback.result_cb({Status::kError}); + callback({Status::kError}); return; } if (!client->IsDiscovering()) { - callback.result_cb({Status::kOutOfOrderApiCall}); + callback({Status::kOutOfOrderApiCall}); return; } GetServiceController()->InjectEndpoint(client, service_id, metadata); - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); } void ServiceControllerRouter::RequestConnection( ClientProxy* client, absl::string_view endpoint_id, const ConnectionRequestInfo& info, - const ConnectionOptions& connection_options, - const ResultCallback& callback) { + const ConnectionOptions& connection_options, ResultCallback callback) { // Cancellations can be fired from clients anytime, need to add the // CancellationListener as soon as possible. client->AddCancellationFlag(std::string(endpoint_id)); @@ -200,10 +202,10 @@ void ServiceControllerRouter::RequestConnection( RouteToServiceController( "scr-request-connection", [this, client, endpoint_id = std::string(endpoint_id), info, - connection_options, callback]() { + connection_options, callback = std::move(callback)]() mutable { if (client->HasPendingConnectionToEndpoint(endpoint_id) || client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kAlreadyConnectedToEndpoint}); + callback({Status::kAlreadyConnectedToEndpoint}); return; } @@ -212,20 +214,21 @@ void ServiceControllerRouter::RequestConnection( if (!status.Ok()) { client->CancelEndpoint(endpoint_id); } - callback.result_cb(status); + callback(status); }); } void ServiceControllerRouter::AcceptConnection(ClientProxy* client, absl::string_view endpoint_id, PayloadListener listener, - const ResultCallback& callback) { + ResultCallback callback) { RouteToServiceController( "scr-accept-connection", [this, client, endpoint_id = std::string(endpoint_id), - listener = std::move(listener), callback]() mutable { + listener = std::move(listener), + callback = std::move(callback)]() mutable { if (client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kAlreadyConnectedToEndpoint}); + callback({Status::kAlreadyConnectedToEndpoint}); return; } @@ -235,25 +238,26 @@ void ServiceControllerRouter::AcceptConnection(ClientProxy* client, << " invoked acceptConnectionRequest() after having already " "accepted/rejected the connection to endpoint(id=" << endpoint_id << ")"; - callback.result_cb({Status::kOutOfOrderApiCall}); + callback({Status::kOutOfOrderApiCall}); return; } - callback.result_cb(GetServiceController()->AcceptConnection( - client, endpoint_id, std::move(listener))); + callback(GetServiceController()->AcceptConnection(client, endpoint_id, + std::move(listener))); }); } void ServiceControllerRouter::RejectConnection(ClientProxy* client, absl::string_view endpoint_id, - const ResultCallback& callback) { + ResultCallback callback) { client->CancelEndpoint(std::string(endpoint_id)); RouteToServiceController( "scr-reject-connection", - [this, client, endpoint_id = std::string(endpoint_id), callback]() { + [this, client, endpoint_id = std::string(endpoint_id), + callback = std::move(callback)]() mutable { if (client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kAlreadyConnectedToEndpoint}); + callback({Status::kAlreadyConnectedToEndpoint}); return; } @@ -263,23 +267,22 @@ void ServiceControllerRouter::RejectConnection(ClientProxy* client, << " invoked rejectConnectionRequest() after having already " "accepted/rejected the connection to endpoint(id=" << endpoint_id << ")"; - callback.result_cb({Status::kOutOfOrderApiCall}); + callback({Status::kOutOfOrderApiCall}); return; } - callback.result_cb( - GetServiceController()->RejectConnection(client, endpoint_id)); + callback(GetServiceController()->RejectConnection(client, endpoint_id)); }); } void ServiceControllerRouter::InitiateBandwidthUpgrade( ClientProxy* client, absl::string_view endpoint_id, - const ResultCallback& callback) { + ResultCallback callback) { RouteToServiceController( - "scr-init-bwu", - [this, client, endpoint_id = std::string(endpoint_id), callback]() { + "scr-init-bwu", [this, client, endpoint_id = std::string(endpoint_id), + callback = std::move(callback)]() mutable { if (!client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kOutOfOrderApiCall}); + callback({Status::kOutOfOrderApiCall}); return; } @@ -287,62 +290,65 @@ void ServiceControllerRouter::InitiateBandwidthUpgrade( // Operation is triggered; the caller can listen to // ConnectionListener::OnBandwidthChanged() to determine its success. - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); } void ServiceControllerRouter::SendPayload( ClientProxy* client, absl::Span endpoint_ids, - Payload payload, const ResultCallback& callback) { + Payload payload, ResultCallback callback) { const std::vector endpoints = std::vector(endpoint_ids.begin(), endpoint_ids.end()); - RouteToServiceController("scr-send-payload", [this, client, - payload = std::move(payload), - endpoints, callback]() mutable { - if (!ClientHasConnectionToAtLeastOneEndpoint(client, endpoints)) { - callback.result_cb({Status::kEndpointUnknown}); - return; - } + RouteToServiceController( + "scr-send-payload", + [this, client, payload = std::move(payload), endpoints, + callback = std::move(callback)]() mutable { + if (!ClientHasConnectionToAtLeastOneEndpoint(client, endpoints)) { + callback({Status::kEndpointUnknown}); + return; + } - GetServiceController()->SendPayload(client, endpoints, std::move(payload)); + GetServiceController()->SendPayload(client, endpoints, + std::move(payload)); - // At this point, we've queued up the send Payload request with the - // ServiceController; any further failures (e.g. one of the endpoints is - // unknown, goes away, or otherwise fails) will be returned to the - // client as a PayloadTransferUpdate. - callback.result_cb({Status::kSuccess}); - }); + // At this point, we've queued up the send Payload request with the + // ServiceController; any further failures (e.g. one of the endpoints is + // unknown, goes away, or otherwise fails) will be returned to the + // client as a PayloadTransferUpdate. + callback({Status::kSuccess}); + }); } void ServiceControllerRouter::CancelPayload(ClientProxy* client, std::uint64_t payload_id, - const ResultCallback& callback) { + ResultCallback callback) { RouteToServiceController( - "scr-cancel-payload", [this, client, payload_id, callback]() { - callback.result_cb( - GetServiceController()->CancelPayload(client, payload_id)); + "scr-cancel-payload", + [this, client, payload_id, callback = std::move(callback)]() mutable { + callback(GetServiceController()->CancelPayload(client, payload_id)); }); } void ServiceControllerRouter::DisconnectFromEndpoint( ClientProxy* client, absl::string_view endpoint_id, - const ResultCallback& callback) { + ResultCallback callback) { // Client can emit the cancellation at anytime, we need to execute the request // without further posting it. client->CancelEndpoint(std::string(endpoint_id)); RouteToServiceController( "scr-disconnect-endpoint", - [this, client, endpoint_id = std::string(endpoint_id), callback]() { + [this, client, endpoint_id = std::string(endpoint_id), + callback = std::move(callback)]() mutable { if (!client->IsConnectedToEndpoint(endpoint_id) && !client->HasPendingConnectionToEndpoint(endpoint_id)) { - callback.result_cb({Status::kOutOfOrderApiCall}); + callback({Status::kOutOfOrderApiCall}); return; } GetServiceController()->DisconnectFromEndpoint(client, endpoint_id); - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); } @@ -388,7 +394,7 @@ void ServiceControllerRouter::StopListeningForIncomingConnectionsV3( void ServiceControllerRouter::RequestConnectionV3( ClientProxy* client, const NearbyDevice& remote_device, v3::ConnectionRequestInfo info, const ConnectionOptions& connection_options, - const ResultCallback& callback) { + ResultCallback callback) { // Cancellations can be fired from clients anytime, need to add the // CancellationListener as soon as possible. client->AddCancellationFlag(remote_device.GetEndpointId()); @@ -396,10 +402,11 @@ void ServiceControllerRouter::RequestConnectionV3( RouteToServiceController( "scr-request-connection", [this, client, endpoint_id = remote_device.GetEndpointId(), - v3_info = std::move(info), connection_options, callback]() mutable { + v3_info = std::move(info), connection_options, + callback = std::move(callback)]() mutable { if (client->HasPendingConnectionToEndpoint(endpoint_id) || client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kAlreadyConnectedToEndpoint}); + callback({Status::kAlreadyConnectedToEndpoint}); return; } @@ -473,19 +480,20 @@ void ServiceControllerRouter::RequestConnectionV3( << endpoint_id << ": " << status.ToString(); client->CancelEndpoint(endpoint_id); } - callback.result_cb(status); + callback(status); }); } void ServiceControllerRouter::AcceptConnectionV3( ClientProxy* client, const NearbyDevice& remote_device, - v3::PayloadListener listener, const ResultCallback& callback) { + v3::PayloadListener listener, ResultCallback callback) { RouteToServiceController( "scr-accept-connection", [this, client, endpoint_id = remote_device.GetEndpointId(), - v3_listener = std::move(listener), callback]() mutable { + v3_listener = std::move(listener), + callback = std::move(callback)]() mutable { if (client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kAlreadyConnectedToEndpoint}); + callback({Status::kAlreadyConnectedToEndpoint}); return; } @@ -495,7 +503,7 @@ void ServiceControllerRouter::AcceptConnectionV3( << " invoked acceptConnectionRequest() after having already " "accepted/rejected the connection to endpoint(id=" << endpoint_id << ")"; - callback.result_cb({Status::kOutOfOrderApiCall}); + callback({Status::kOutOfOrderApiCall}); return; } @@ -513,21 +521,22 @@ void ServiceControllerRouter::AcceptConnectionV3( v3_cb(v3::ConnectionsDevice(endpoint_id, "", {}), info); }}; - callback.result_cb(GetServiceController()->AcceptConnection( + callback(GetServiceController()->AcceptConnection( client, endpoint_id, std::move(old_listener))); }); } void ServiceControllerRouter::RejectConnectionV3( ClientProxy* client, const NearbyDevice& remote_device, - const ResultCallback& callback) { + ResultCallback callback) { client->CancelEndpoint(remote_device.GetEndpointId()); RouteToServiceController( "scr-reject-connection", - [this, client, endpoint_id = remote_device.GetEndpointId(), callback]() { + [this, client, endpoint_id = remote_device.GetEndpointId(), + callback = std::move(callback)]() mutable { if (client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kAlreadyConnectedToEndpoint}); + callback({Status::kAlreadyConnectedToEndpoint}); return; } @@ -537,23 +546,23 @@ void ServiceControllerRouter::RejectConnectionV3( << " invoked rejectConnectionRequest() after having already " "accepted/rejected the connection to endpoint(id=" << endpoint_id << ")"; - callback.result_cb({Status::kOutOfOrderApiCall}); + callback({Status::kOutOfOrderApiCall}); return; } - callback.result_cb( - GetServiceController()->RejectConnection(client, endpoint_id)); + callback(GetServiceController()->RejectConnection(client, endpoint_id)); }); } void ServiceControllerRouter::InitiateBandwidthUpgradeV3( ClientProxy* client, const NearbyDevice& remote_device, - const ResultCallback& callback) { + ResultCallback callback) { RouteToServiceController( "scr-init-bwu", - [this, client, endpoint_id = remote_device.GetEndpointId(), callback]() { + [this, client, endpoint_id = remote_device.GetEndpointId(), + callback = std::move(callback)]() mutable { if (!client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kOutOfOrderApiCall}); + callback({Status::kOutOfOrderApiCall}); return; } @@ -561,19 +570,19 @@ void ServiceControllerRouter::InitiateBandwidthUpgradeV3( // Operation is triggered; the caller can listen to // ConnectionListener::OnBandwidthChanged() to determine its success. - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); } void ServiceControllerRouter::SendPayloadV3( ClientProxy* client, const NearbyDevice& recipient_device, Payload payload, - const ResultCallback& callback) { + ResultCallback callback) { RouteToServiceController( - "scr-send-payload", - [this, client, payload = std::move(payload), - endpoint_id = recipient_device.GetEndpointId(), callback]() mutable { + "scr-send-payload", [this, client, payload = std::move(payload), + endpoint_id = recipient_device.GetEndpointId(), + callback = std::move(callback)]() mutable { if (!client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kEndpointUnknown}); + callback({Status::kEndpointUnknown}); return; } @@ -584,90 +593,94 @@ void ServiceControllerRouter::SendPayloadV3( // ServiceController; any further failures (e.g. one of the endpoints is // unknown, goes away, or otherwise fails) will be returned to the // client as a PayloadTransferUpdate. - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); } void ServiceControllerRouter::CancelPayloadV3( ClientProxy* client, const NearbyDevice& recipient_device, - uint64_t payload_id, const ResultCallback& callback) { + uint64_t payload_id, ResultCallback callback) { RouteToServiceController( - "scr-cancel-payload", [this, client, payload_id, callback]() { - callback.result_cb( - GetServiceController()->CancelPayload(client, payload_id)); + "scr-cancel-payload", + [this, client, payload_id, callback = std::move(callback)]() mutable { + callback(GetServiceController()->CancelPayload(client, payload_id)); }); } void ServiceControllerRouter::DisconnectFromDeviceV3( ClientProxy* client, const NearbyDevice& remote_device, - const ResultCallback& callback) { + ResultCallback callback) { // Client can emit the cancellation at anytime, we need to execute the request // without further posting it. client->CancelEndpoint(remote_device.GetEndpointId()); RouteToServiceController( "scr-disconnect-endpoint", - [this, client, endpoint_id = remote_device.GetEndpointId(), callback]() { + [this, client, endpoint_id = remote_device.GetEndpointId(), + callback = std::move(callback)]() mutable { if (!client->IsConnectedToEndpoint(endpoint_id) && !client->HasPendingConnectionToEndpoint(endpoint_id)) { - callback.result_cb({Status::kOutOfOrderApiCall}); + callback({Status::kOutOfOrderApiCall}); return; } GetServiceController()->DisconnectFromEndpoint(client, endpoint_id); - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); } void ServiceControllerRouter::UpdateAdvertisingOptionsV3( ClientProxy* client, absl::string_view service_id, - const AdvertisingOptions& options, const ResultCallback& callback) { + const AdvertisingOptions& options, ResultCallback callback) { RouteToServiceController( "scr-update-advertising-options", - [this, client, options, callback, service_id]() { - callback.result_cb(GetServiceController()->UpdateAdvertisingOptions( + [this, client, options, callback = std::move(callback), + service_id]() mutable { + callback(GetServiceController()->UpdateAdvertisingOptions( client, service_id, options)); }); } void ServiceControllerRouter::UpdateDiscoveryOptionsV3( ClientProxy* client, absl::string_view service_id, - const DiscoveryOptions& options, const ResultCallback& callback) { + const DiscoveryOptions& options, ResultCallback callback) { RouteToServiceController( "scr-update-discovery-options", - [this, client, options, callback, service_id]() { - callback.result_cb(GetServiceController()->UpdateDiscoveryOptions( + [this, client, options, callback = std::move(callback), + service_id]() mutable { + callback(GetServiceController()->UpdateDiscoveryOptions( client, service_id, options)); }); } void ServiceControllerRouter::StopAllEndpoints(ClientProxy* client, - const ResultCallback& callback) { + ResultCallback callback) { // Client can emit the cancellation at anytime, we need to execute the request // without further posting it. client->CancelAllEndpoints(); RouteToServiceController( - "scr-stop-all-endpoints", [this, client, callback]() { + "scr-stop-all-endpoints", + [this, client, callback = std::move(callback)]() mutable { NEARBY_LOGS(INFO) << "Client " << client->GetClientId() << " has requested us to stop all endpoints. We will " "now reset the client."; FinishClientSession(client); - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); } -void ServiceControllerRouter::SetCustomSavePath( - ClientProxy* client, absl::string_view path, - const ResultCallback& callback) { +void ServiceControllerRouter::SetCustomSavePath(ClientProxy* client, + absl::string_view path, + ResultCallback callback) { RouteToServiceController( - "scr-set-custom-save-path", - [this, client, path = std::string(path), callback]() { + "scr-set-custom-save-path", [this, client, path = std::string(path), + callback = std::move(callback)]() mutable { NEARBY_LOGS(INFO) << "Client " << client->GetClientId() << " has requested us to set custom save path to " << path; GetServiceController()->SetCustomSavePath(client, path); - callback.result_cb({Status::kSuccess}); + callback({Status::kSuccess}); }); } diff --git a/connections/implementation/service_controller_router.h b/connections/implementation/service_controller_router.h index 064274e7..9b3ee470 100644 --- a/connections/implementation/service_controller_router.h +++ b/connections/implementation/service_controller_router.h @@ -70,52 +70,50 @@ class ServiceControllerRouter { absl::string_view service_id, const AdvertisingOptions& advertising_options, const ConnectionRequestInfo& info, - const ResultCallback& callback); + ResultCallback callback); - virtual void StopAdvertising(ClientProxy* client, - const ResultCallback& callback); + virtual void StopAdvertising(ClientProxy* client, ResultCallback callback); virtual void StartDiscovery(ClientProxy* client, absl::string_view service_id, const DiscoveryOptions& discovery_options, const DiscoveryListener& listener, - const ResultCallback& callback); + ResultCallback callback); - virtual void StopDiscovery(ClientProxy* client, - const ResultCallback& callback); + virtual void StopDiscovery(ClientProxy* client, ResultCallback callback); virtual void InjectEndpoint(ClientProxy* client, absl::string_view service_id, const OutOfBandConnectionMetadata& metadata, - const ResultCallback& callback); + ResultCallback callback); virtual void RequestConnection(ClientProxy* client, absl::string_view endpoint_id, const ConnectionRequestInfo& info, const ConnectionOptions& connection_options, - const ResultCallback& callback); + ResultCallback callback); virtual void AcceptConnection(ClientProxy* client, absl::string_view endpoint_id, PayloadListener listener, - const ResultCallback& callback); + ResultCallback callback); virtual void RejectConnection(ClientProxy* client, absl::string_view endpoint_id, - const ResultCallback& callback); + ResultCallback callback); virtual void InitiateBandwidthUpgrade(ClientProxy* client, absl::string_view endpoint_id, - const ResultCallback& callback); + ResultCallback callback); virtual void SendPayload(ClientProxy* client, absl::Span endpoint_ids, - Payload payload, const ResultCallback& callback); + Payload payload, ResultCallback callback); virtual void CancelPayload(ClientProxy* client, std::uint64_t payload_id, - const ResultCallback& callback); + ResultCallback callback); virtual void DisconnectFromEndpoint(ClientProxy* client, absl::string_view endpoint_id, - const ResultCallback& callback); + ResultCallback callback); ////////////////////////////// V3 //////////////////////////////////////////// virtual void StartListeningForIncomingConnectionsV3( @@ -130,50 +128,47 @@ class ServiceControllerRouter { const NearbyDevice& remote_device, v3::ConnectionRequestInfo info, const ConnectionOptions& connection_options, - const ResultCallback& callback); + ResultCallback callback); virtual void AcceptConnectionV3(ClientProxy* client, const NearbyDevice& remote_device, v3::PayloadListener listener, - const ResultCallback& callback); + ResultCallback callback); virtual void RejectConnectionV3(ClientProxy* client, const NearbyDevice& remote_device, - const ResultCallback& callback); + ResultCallback callback); virtual void InitiateBandwidthUpgradeV3(ClientProxy* client, const NearbyDevice& remote_device, - const ResultCallback& callback); + ResultCallback callback); virtual void SendPayloadV3(ClientProxy* client, const NearbyDevice& recipient_device, - Payload payload, const ResultCallback& callback); + Payload payload, ResultCallback callback); virtual void CancelPayloadV3(ClientProxy* client, const NearbyDevice& recipient_device, std::uint64_t payload_id, - const ResultCallback& callback); + ResultCallback callback); virtual void DisconnectFromDeviceV3(ClientProxy* client, const NearbyDevice& remote_device, - const ResultCallback& callback); + ResultCallback callback); virtual void UpdateAdvertisingOptionsV3( ClientProxy* client, absl::string_view service_id, - const AdvertisingOptions& advertising_options, - const ResultCallback& callback); + const AdvertisingOptions& advertising_options, ResultCallback callback); virtual void UpdateDiscoveryOptionsV3( ClientProxy* client, absl::string_view service_id, - const DiscoveryOptions& discovery_options, - const ResultCallback& callback); + const DiscoveryOptions& discovery_options, ResultCallback callback); /////////////////////////////// END V3 /////////////////////////////////////// - virtual void StopAllEndpoints(ClientProxy* client, - const ResultCallback& callback); + virtual void StopAllEndpoints(ClientProxy* client, ResultCallback callback); virtual void SetCustomSavePath(ClientProxy* client, absl::string_view path, - const ResultCallback& callback); + ResultCallback callback); void SetServiceControllerForTesting( std::unique_ptr service_controller); diff --git a/connections/implementation/service_controller_router_test.cc b/connections/implementation/service_controller_router_test.cc index 2d235850..be3c7e0a 100644 --- a/connections/implementation/service_controller_router_test.cc +++ b/connections/implementation/service_controller_router_test.cc @@ -81,7 +81,7 @@ class ServiceControllerRouterTest : public testing::Test { MutexLock lock(&mutex_); complete_ = false; router_.StartAdvertising(client, service_id, advertising_options, info, - callback); + std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -95,7 +95,7 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.StopAdvertising(client, callback); + router_.StopAdvertising(client, std::move(callback)); while (!complete_) cond_.Wait(); } client->StoppedAdvertising(); @@ -105,14 +105,14 @@ class ServiceControllerRouterTest : public testing::Test { void StartDiscovery(ClientProxy* client, std::string service_id, DiscoveryOptions discovery_options, const DiscoveryListener& listener, - const ResultCallback& callback) { + ResultCallback callback) { EXPECT_CALL(*mock_, StartDiscovery) .WillOnce(Return(Status{Status::kSuccess})); { MutexLock lock(&mutex_); complete_ = false; router_.StartDiscovery(client, kServiceId, discovery_options, listener, - callback); + std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -126,7 +126,7 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.StopDiscovery(client, callback); + router_.StopDiscovery(client, std::move(callback)); while (!complete_) cond_.Wait(); } client->StoppedDiscovery(); @@ -140,7 +140,7 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.InjectEndpoint(client, service_id, metadata, callback); + router_.InjectEndpoint(client, service_id, metadata, std::move(callback)); while (!complete_) cond_.Wait(); } } @@ -155,7 +155,7 @@ class ServiceControllerRouterTest : public testing::Test { MutexLock lock(&mutex_); complete_ = false; router_.RequestConnection(client, endpoint_id, request_info, - connection_options, callback); + connection_options, std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -173,7 +173,7 @@ class ServiceControllerRouterTest : public testing::Test { } void AcceptConnection(ClientProxy* client, const std::string endpoint_id, - const ResultCallback& callback) { + ResultCallback callback) { EXPECT_CALL(*mock_, AcceptConnection) .WillOnce(Return(Status{Status::kSuccess})); // Pre-condition for successful Accept is: connection must exist. @@ -181,8 +181,7 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.AcceptConnection(client, endpoint_id, {}, - callback); + router_.AcceptConnection(client, endpoint_id, {}, std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -203,7 +202,7 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.RejectConnection(client, endpoint_id, callback); + router_.RejectConnection(client, endpoint_id, std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -219,7 +218,8 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.InitiateBandwidthUpgrade(client, endpoint_id, callback); + router_.InitiateBandwidthUpgrade(client, endpoint_id, + std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -239,7 +239,7 @@ class ServiceControllerRouterTest : public testing::Test { MutexLock lock(&mutex_); complete_ = false; router_.SendPayload(client, absl::MakeSpan(endpoint_ids), - std::move(payload), callback); + std::move(payload), std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -252,7 +252,7 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.CancelPayload(client, payload_id, callback); + router_.CancelPayload(client, payload_id, std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -266,7 +266,7 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.DisconnectFromEndpoint(client, endpoint_id, callback); + router_.DisconnectFromEndpoint(client, endpoint_id, std::move(callback)); while (!complete_) cond_.Wait(); } client->OnDisconnected(endpoint_id, false); @@ -306,7 +306,7 @@ class ServiceControllerRouterTest : public testing::Test { complete_ = false; router_.RequestConnectionV3(client, kRemoteDevice, std::move(request_info), connection_options, - callback); + std::move(callback)); while (!complete_) cond_.Wait(); if (check_result) { EXPECT_EQ(result_, Status{Status::kSuccess}); @@ -330,7 +330,7 @@ class ServiceControllerRouterTest : public testing::Test { void AcceptConnectionV3(ClientProxy* client, const NearbyDevice& kRemoteDevice, - const ResultCallback& callback) { + ResultCallback callback) { EXPECT_CALL(*mock_, AcceptConnection) .WillOnce(Return(Status{Status::kSuccess})); // Pre-condition for successful Accept is: connection must exist. @@ -339,7 +339,8 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.AcceptConnectionV3(client, kRemoteDevice, {}, callback); + router_.AcceptConnectionV3(client, kRemoteDevice, {}, + std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -360,7 +361,7 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.RejectConnectionV3(client, device, callback); + router_.RejectConnectionV3(client, device, std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -376,7 +377,7 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.InitiateBandwidthUpgradeV3(client, device, callback); + router_.InitiateBandwidthUpgradeV3(client, device, std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -393,7 +394,7 @@ class ServiceControllerRouterTest : public testing::Test { MutexLock lock(&mutex_); complete_ = false; router_.SendPayloadV3(client, recipient_device, std::move(payload), - callback); + std::move(callback)); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } @@ -401,13 +402,14 @@ class ServiceControllerRouterTest : public testing::Test { void CancelPayloadV3(ClientProxy* client, const NearbyDevice& recipient_device, - uint64_t payload_id, const ResultCallback& callback) { + uint64_t payload_id, ResultCallback callback) { EXPECT_CALL(*mock_, CancelPayload).Times(1); EXPECT_TRUE( client->IsConnectedToEndpoint(recipient_device.GetEndpointId())); { MutexLock lock(&mutex_); - router_.CancelPayloadV3(client, recipient_device, payload_id, callback); + router_.CancelPayloadV3(client, recipient_device, payload_id, + std::move(callback)); } } @@ -419,7 +421,8 @@ class ServiceControllerRouterTest : public testing::Test { { MutexLock lock(&mutex_); complete_ = false; - router_.DisconnectFromDeviceV3(client, kRemoteDevice, callback); + router_.DisconnectFromDeviceV3(client, kRemoteDevice, + std::move(callback)); while (!complete_) cond_.Wait(); } client->OnDisconnected(kRemoteDevice.GetEndpointId(), false); @@ -455,15 +458,6 @@ class ServiceControllerRouterTest : public testing::Test { } protected: - const ResultCallback kCallback{ - .result_cb = - [this](Status status) { - MutexLock lock(&mutex_); - result_ = status; - complete_ = true; - cond_.Notify(); - }, - }; const std::string kServiceId = "service id"; const std::string kRequestorName = "requestor name"; const std::string kRemoteEndpointId = "remote endpoint id"; @@ -542,122 +536,293 @@ TEST_F(ServiceControllerRouterTest, QualityConversionWorks) { TEST_F(ServiceControllerRouterTest, StartAdvertisingCalled) { StartAdvertising(&client_, kServiceId, kAdvertisingOptions, - kConnectionRequestInfo, kCallback); + kConnectionRequestInfo, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, StopAdvertisingCalled) { StartAdvertising(&client_, kServiceId, kAdvertisingOptions, - kConnectionRequestInfo, kCallback); - StopAdvertising(&client_, kCallback); + kConnectionRequestInfo, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); + StopAdvertising(&client_, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, StartDiscoveryCalled) { StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, StopDiscoveryCalled) { StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); - StopDiscovery(&client_, kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); + StopDiscovery(&client_, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, InjectEndpointCalled) { StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); - InjectEndpoint(&client_, kServiceId, kOutOfBandConnectionMetadata, kCallback); - StopDiscovery(&client_, kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); + InjectEndpoint(&client_, kServiceId, kOutOfBandConnectionMetadata, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); + StopDiscovery(&client_, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, RequestConnectionCalled) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); RequestConnection(&client_, kRemoteEndpointId, kConnectionRequestInfo, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, AcceptConnectionCalled) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. RequestConnection(&client_, kRemoteEndpointId, kConnectionRequestInfo, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now, we can accept connection. - AcceptConnection(&client_, kRemoteEndpointId, kCallback); + AcceptConnection(&client_, kRemoteEndpointId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, RejectConnectionCalled) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. RequestConnection(&client_, kRemoteEndpointId, kConnectionRequestInfo, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now, we can reject connection. - RejectConnection(&client_, kRemoteEndpointId, kCallback); + RejectConnection(&client_, kRemoteEndpointId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, InitiateBandwidthUpgradeCalled) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. RequestConnection(&client_, kRemoteEndpointId, kConnectionRequestInfo, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now, we can accept connection. - AcceptConnection(&client_, kRemoteEndpointId, kCallback); + AcceptConnection(&client_, kRemoteEndpointId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now we can change connection bandwidth. - InitiateBandwidthUpgrade(&client_, kRemoteEndpointId, kCallback); + InitiateBandwidthUpgrade(&client_, kRemoteEndpointId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, SendPayloadCalled) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. RequestConnection(&client_, kRemoteEndpointId, kConnectionRequestInfo, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now, we can accept connection. - AcceptConnection(&client_, kRemoteEndpointId, kCallback); + AcceptConnection(&client_, kRemoteEndpointId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now we can send payload. SendPayload(&client_, std::vector{kRemoteEndpointId}, - Payload{ByteArray("data")}, kCallback); + Payload{ByteArray("data")}, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, CancelPayloadCalled) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. RequestConnection(&client_, kRemoteEndpointId, kConnectionRequestInfo, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now, we can accept connection. - AcceptConnection(&client_, kRemoteEndpointId, kCallback); + AcceptConnection(&client_, kRemoteEndpointId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // We have to know payload id, before we can cancel payload transfer. // It is either after a call to SendPayload, or after receiving // PayloadProgress callback. Let's assume we have it, and proceed. - CancelPayload(&client_, kPayloadId, kCallback); + CancelPayload(&client_, kPayloadId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, DisconnectFromEndpointCalled) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. RequestConnection(&client_, kRemoteEndpointId, kConnectionRequestInfo, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now, we can accept connection. - AcceptConnection(&client_, kRemoteEndpointId, kCallback); + AcceptConnection(&client_, kRemoteEndpointId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // We can disconnect at any time after RequestConnection. - DisconnectFromEndpoint(&client_, kRemoteEndpointId, kCallback); + DisconnectFromEndpoint(&client_, kRemoteEndpointId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, RequestConnectionCalledV3) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. auto local_device = v3::ConnectionsDevice(client_.GetLocalEndpointId(), kRequestorName, {}); @@ -690,7 +855,13 @@ TEST_F(ServiceControllerRouterTest, RequestConnectionCalledV3) { bandwidth_changed_latch.CountDown(); }}, }, - kCallback, true); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + true); EXPECT_TRUE(initiated_latch.Await().Ok()); EXPECT_TRUE(result_latch.Await().Ok()); EXPECT_TRUE(disconnected_latch.Await().Ok()); @@ -700,7 +871,12 @@ TEST_F(ServiceControllerRouterTest, RequestConnectionCalledV3) { TEST_F(ServiceControllerRouterTest, RequestConnectionV3FakeDevice) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. auto local_device = FakeNearbyDevice(); // Testing callback wrapping as well. @@ -732,7 +908,13 @@ TEST_F(ServiceControllerRouterTest, RequestConnectionV3FakeDevice) { bandwidth_changed_latch.CountDown(); }}, }, - kCallback, true, true, false); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + true, true, false); EXPECT_TRUE(initiated_latch.Await().Ok()); EXPECT_TRUE(result_latch.Await().Ok()); EXPECT_TRUE(disconnected_latch.Await().Ok()); @@ -742,22 +924,41 @@ TEST_F(ServiceControllerRouterTest, RequestConnectionV3FakeDevice) { TEST_F(ServiceControllerRouterTest, RequestConnectionV3TwiceFails) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. auto local_device = v3::ConnectionsDevice(client_.GetLocalEndpointId(), kRequestorName, {}); - RequestConnectionV3(&client_, kRemoteDevice, - v3::ConnectionRequestInfo{ - .local_device = local_device, - .listener = {}, - }, - kCallback, false); - RequestConnectionV3(&client_, kRemoteDevice, - v3::ConnectionRequestInfo{ - .local_device = local_device, - .listener = {}, - }, - kCallback, false, false); + RequestConnectionV3( + &client_, kRemoteDevice, + v3::ConnectionRequestInfo{ + .local_device = local_device, + .listener = {}, + }, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + false); + RequestConnectionV3( + &client_, kRemoteDevice, + v3::ConnectionRequestInfo{ + .local_device = local_device, + .listener = {}, + }, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + false, false); { MutexLock lock(&mutex_); EXPECT_EQ(result_.value, Status::kAlreadyConnectedToEndpoint); @@ -767,113 +968,236 @@ TEST_F(ServiceControllerRouterTest, RequestConnectionV3TwiceFails) { TEST_F(ServiceControllerRouterTest, AcceptConnectionCalledV3) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. auto local_device = v3::ConnectionsDevice(client_.GetLocalEndpointId(), kRequestorName, {}); - RequestConnectionV3(&client_, kRemoteDevice, - v3::ConnectionRequestInfo{ - .local_device = local_device, - .listener = {}, - }, - kCallback, false); + RequestConnectionV3( + &client_, kRemoteDevice, + v3::ConnectionRequestInfo{ + .local_device = local_device, + .listener = {}, + }, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + false); // Now, we can accept connection. - AcceptConnectionV3(&client_, kRemoteDevice, kCallback); + AcceptConnectionV3(&client_, kRemoteDevice, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, RejectConnectionCalledV3) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. auto local_device = v3::ConnectionsDevice(client_.GetLocalEndpointId(), kRequestorName, {}); - RequestConnectionV3(&client_, kRemoteDevice, - v3::ConnectionRequestInfo{ - .local_device = local_device, - .listener = {}, - }, - kCallback, false); + RequestConnectionV3( + &client_, kRemoteDevice, + v3::ConnectionRequestInfo{ + .local_device = local_device, + .listener = {}, + }, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + false); // Now, we can reject connection. - RejectConnectionV3(&client_, kRemoteDevice, kCallback); + RejectConnectionV3(&client_, kRemoteDevice, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, InitiateBandwidthUpgradeCalledV3) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. auto local_device = v3::ConnectionsDevice(client_.GetLocalEndpointId(), kRequestorName, {}); - RequestConnectionV3(&client_, kRemoteDevice, - v3::ConnectionRequestInfo{ - .local_device = local_device, - .listener = {}, - }, - kCallback, false); + RequestConnectionV3( + &client_, kRemoteDevice, + v3::ConnectionRequestInfo{ + .local_device = local_device, + .listener = {}, + }, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + false); // Now, we can accept connection. - AcceptConnectionV3(&client_, kRemoteDevice, kCallback); + AcceptConnectionV3(&client_, kRemoteDevice, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now we can change connection bandwidth. - InitiateBandwidthUpgradeV3(&client_, kRemoteDevice, kCallback); + InitiateBandwidthUpgradeV3(&client_, kRemoteDevice, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, SendPayloadCalledV3) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. auto local_device = v3::ConnectionsDevice(client_.GetLocalEndpointId(), kRequestorName, {}); - RequestConnectionV3(&client_, kRemoteDevice, - v3::ConnectionRequestInfo{ - .local_device = local_device, - .listener = {}, - }, - kCallback, false); + RequestConnectionV3( + &client_, kRemoteDevice, + v3::ConnectionRequestInfo{ + .local_device = local_device, + .listener = {}, + }, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + false); // Now, we can accept connection. - AcceptConnectionV3(&client_, kRemoteDevice, kCallback); + AcceptConnectionV3(&client_, kRemoteDevice, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Now we can send payload. - SendPayloadV3(&client_, kRemoteDevice, Payload{ByteArray("data")}, kCallback); + SendPayloadV3(&client_, kRemoteDevice, Payload{ByteArray("data")}, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, DisconnectFromDeviceCalledV3) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. auto local_device = v3::ConnectionsDevice(client_.GetLocalEndpointId(), kRequestorName, {}); - RequestConnectionV3(&client_, kRemoteDevice, - v3::ConnectionRequestInfo{ - .local_device = local_device, - .listener = {}, - }, - kCallback, false); + RequestConnectionV3( + &client_, kRemoteDevice, + v3::ConnectionRequestInfo{ + .local_device = local_device, + .listener = {}, + }, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + false); // Now, we can accept connection. - AcceptConnectionV3(&client_, kRemoteDevice, kCallback); + AcceptConnectionV3(&client_, kRemoteDevice, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // We can disconnect at any time after RequestConnection. - DisconnectFromDeviceV3(&client_, kRemoteDevice, kCallback); + DisconnectFromDeviceV3(&client_, kRemoteDevice, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, CancelPayloadV3Called) { // Either Advertising, or Discovery should be ongoing. StartDiscovery(&client_, kServiceId, kDiscoveryOptions, discovery_listener_, - kCallback); + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // Establish connection. auto local_device = v3::ConnectionsDevice(client_.GetLocalEndpointId(), kRequestorName, {}); - RequestConnectionV3(&client_, kRemoteDevice, - v3::ConnectionRequestInfo{ - .local_device = local_device, - .listener = {}, - }, - kCallback, false); + RequestConnectionV3( + &client_, kRemoteDevice, + v3::ConnectionRequestInfo{ + .local_device = local_device, + .listener = {}, + }, + [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }, + false); // Now, we can accept connection. - AcceptConnectionV3(&client_, kRemoteDevice, kCallback); + AcceptConnectionV3(&client_, kRemoteDevice, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); // We have to know payload id, before we can cancel payload transfer. // It is either after a call to SendPayload, or after receiving // PayloadProgress callback. Let's assume we have it, and proceed. - CancelPayloadV3(&client_, kRemoteDevice, kPayloadId, kCallback); + CancelPayloadV3(&client_, kRemoteDevice, kPayloadId, [this](Status status) { + MutexLock lock(&mutex_); + result_ = status; + complete_ = true; + cond_.Notify(); + }); } TEST_F(ServiceControllerRouterTest, diff --git a/connections/listeners.h b/connections/listeners.h index 92bb76b3..15ed72f7 100644 --- a/connections/listeners.h +++ b/connections/listeners.h @@ -42,12 +42,9 @@ namespace connections { // This is not the same as completion of the associated process, // which may have many states, and multiple async jobs, and be still ongoing. // Progress on the overall process is reported by the associated listener. -struct ResultCallback { - // Callback to access the status of the operation when available. - // status - result of job execution; - // Status::kSuccess, if successful; anything else indicates failure. - std::function result_cb = [](Status) {}; -}; +// status - result of job execution; +// Status::kSuccess, if successful; anything else indicates failure. +using ResultCallback = absl::AnyInvocable; struct ConnectionResponseInfo { std::string GetAuthenticationDigits() { diff --git a/connections/swift/NearbyCoreAdapter/Sources/GNCCoreAdapter.mm b/connections/swift/NearbyCoreAdapter/Sources/GNCCoreAdapter.mm index a62da260..e4395758 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/GNCCoreAdapter.mm +++ b/connections/swift/NearbyCoreAdapter/Sources/GNCCoreAdapter.mm @@ -162,27 +162,26 @@ GNCStatus GNCStatusFromCppStatus(Status status) { ByteArray((const char *)endpointInfo.bytes, endpointInfo.length); connection_request_info.listener = std::move(listener); - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->StartAdvertising(service_id, advertising_options, connection_request_info, result); + _core->StartAdvertising(service_id, advertising_options, connection_request_info, + std::move(result)); } - (void)stopAdvertisingWithCompletionHandler:(void (^)(NSError *error))completionHandler { - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->StopAdvertising(result); + _core->StopAdvertising(std::move(result)); } - (void)startDiscoveryAsService:(NSString *)serviceID @@ -205,27 +204,25 @@ GNCStatus GNCStatusFromCppStatus(Status status) { [delegate lostEndpoint:endpointID]; }; - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->StartDiscovery(service_id, discovery_options, std::move(listener), result); + _core->StartDiscovery(service_id, discovery_options, std::move(listener), std::move(result)); } - (void)stopDiscoveryWithCompletionHandler:(void (^)(NSError *error))completionHandler { - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->StopDiscovery(result); + _core->StopDiscovery(std::move(result)); } - (void)requestConnectionToEndpoint:(NSString *)endpointID @@ -265,15 +262,15 @@ GNCStatus GNCStatusFromCppStatus(Status status) { ConnectionOptions connection_options = [connectionOptions toCpp]; - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->RequestConnection(endpoint_id, connection_request_info, connection_options, result); + _core->RequestConnection(endpoint_id, connection_request_info, connection_options, + std::move(result)); } - (void)acceptConnectionRequestFromEndpoint:(NSString *)endpointID @@ -312,30 +309,28 @@ GNCStatus GNCStatusFromCppStatus(Status status) { totalBytes:info.total_bytes]; }; - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->AcceptConnection(endpoint_id, std::move(listener), result); + _core->AcceptConnection(endpoint_id, std::move(listener), std::move(result)); } - (void)rejectConnectionRequestFromEndpoint:(NSString *)endpointID withCompletionHandler:(void (^)(NSError *error))completionHandler { std::string endpoint_id = [endpointID cStringUsingEncoding:[NSString defaultCStringEncoding]]; - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->RejectConnection(endpoint_id, result); + _core->RejectConnection(endpoint_id, std::move(result)); } - (void)sendPayload:(GNCPayload *)payload @@ -348,69 +343,64 @@ GNCStatus GNCStatusFromCppStatus(Status status) { endpoint_ids.push_back(endpoint_id); } - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->SendPayload(endpoint_ids, [payload toCpp], result); + _core->SendPayload(endpoint_ids, [payload toCpp], std::move(result)); } - (void)cancelPayload:(int64_t)payloadID withCompletionHandler:(void (^)(NSError *error))completionHandler { - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->CancelPayload(payloadID, result); + _core->CancelPayload(payloadID, std::move(result)); } - (void)disconnectFromEndpoint:(NSString *)endpointID withCompletionHandler:(void (^)(NSError *error))completionHandler { std::string endpoint_id = [endpointID cStringUsingEncoding:[NSString defaultCStringEncoding]]; - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->DisconnectFromEndpoint(endpoint_id, result); + _core->DisconnectFromEndpoint(endpoint_id, std::move(result)); } - (void)stopAllEndpointsWithCompletionHandler:(void (^)(NSError *error))completionHandler { - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->StopAllEndpoints(result); + _core->StopAllEndpoints(std::move(result)); } - (void)initiateBandwidthUpgrade:(NSString *)endpointID withCompletionHandler:(void (^)(NSError *error))completionHandler { std::string endpoint_id = [endpointID cStringUsingEncoding:[NSString defaultCStringEncoding]]; - ResultListener result; - result.result_cb = ^(Status status) { + ResultListener result = [completionHandler](Status status) { NSError *err = NSErrorFromCppStatus(status); if (completionHandler) { completionHandler(err); } }; - _core->InitiateBandwidthUpgrade(endpoint_id, result); + _core->InitiateBandwidthUpgrade(endpoint_id, std::move(result)); } - (NSString *)localEndpointID { From 562483cb44816078f10a6d925dba9a5a763627f6 Mon Sep 17 00:00:00 2001 From: Hai Shang Date: Wed, 26 Jul 2023 11:00:57 -0700 Subject: [PATCH 017/128] update sensor fusion api to favor std::optional over absl::optional PiperOrigin-RevId: 551257674 --- presence/implementation/BUILD | 1 - presence/implementation/sensor_fusion.h | 17 +++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/presence/implementation/BUILD b/presence/implementation/BUILD index a3da0f81..6f142289 100644 --- a/presence/implementation/BUILD +++ b/presence/implementation/BUILD @@ -127,7 +127,6 @@ cc_library( deps = [ "//presence:types", "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/types:optional", ], ) diff --git a/presence/implementation/sensor_fusion.h b/presence/implementation/sensor_fusion.h index 027f2225..6d56e3ce 100644 --- a/presence/implementation/sensor_fusion.h +++ b/presence/implementation/sensor_fusion.h @@ -17,10 +17,10 @@ #include #include +#include #include #include "absl/functional/any_invocable.h" -#include "absl/types/optional.h" #include "presence/device_motion.h" #include "presence/presence_zone.h" @@ -42,8 +42,8 @@ struct RangingMeasurement { struct RangingPosition { RangingMeasurement distance; - absl::optional azimuth; - absl::optional elevation; + std::optional azimuth; + std::optional elevation; uint64_t elapsed_realtime_millis; }; @@ -55,7 +55,7 @@ struct ZoneTransition { struct RangingData { DataSource data_source; RangingPosition position; - absl::optional zone_transition; + std::optional zone_transition; std::vector device_motions; }; @@ -99,13 +99,13 @@ class SensorFusion { * * @param device_id A unique device id of the peer device. * @param txPower Calibrated TX power of the scan result, {@code - * absl::nullopt} if the calibrated TX power is not available. + * 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 void updateBleScanResult(uint64_t device_id, - absl::optional txPower, int rssi, + std::optional txPower, int rssi, uint64_t elapsed_realtime_millis); /** @@ -139,13 +139,14 @@ class SensorFusion { /** * Returns the best ranging estimate to a given device. Returns {@code - * absl::nullopt} if the sensor fusion cannot produce a ranging estimate. + * std::nullopt} if the sensor fusion cannot produce a ranging estimate. * * @param device_id Id of the peer device. */ - virtual absl::optional getRangingData(uint64_t device_id); + virtual std::optional getRangingData(uint64_t device_id); }; } // namespace presence } // namespace nearby #endif // THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_SENSOR_FUSION_H_ + From 0e7a8181b0d72d29111acf6b208044f28e5c1829 Mon Sep 17 00:00:00 2001 From: Hai Shang Date: Wed, 26 Jul 2023 13:04:57 -0700 Subject: [PATCH 018/128] internal clean up PiperOrigin-RevId: 551292810 --- presence/implementation/sensor_fusion.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/presence/implementation/sensor_fusion.h b/presence/implementation/sensor_fusion.h index 6d56e3ce..facec76e 100644 --- a/presence/implementation/sensor_fusion.h +++ b/presence/implementation/sensor_fusion.h @@ -90,9 +90,9 @@ class SensorFusion { * source query. * @param available_sources A bit mask of data sources that are available. */ - virtual std::vector getDataSources( + virtual std::vector GetDataSources( uint64_t elapsed_realtime_millis, - std::vector available_sources); + const std::vector& available_sources); /** * Updates BLE scanned results to Sensor Fusion. @@ -104,7 +104,7 @@ class SensorFusion { * @param elapsed_realtime_millis Elapsed timestamp since boot when the * scan result is discovered. */ - virtual void updateBleScanResult(uint64_t device_id, + virtual void UpdateBleScanResult(uint64_t device_id, std::optional txPower, int rssi, uint64_t elapsed_realtime_millis); @@ -114,28 +114,28 @@ class SensorFusion { * @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, + virtual void UpdateUwbRangingResult(uint64_t device_id, RangingPosition position); /** * Adds callback for updates of proximity zone transitions. */ - virtual void requestZoneTransitionUpdates(ZoneTransitionCallback callback); + virtual void RequestZoneTransitionUpdates(ZoneTransitionCallback callback); /** * Removes callback for updates of proximity zone transitions. */ - virtual void removeZoneTransitionUpdates(ZoneTransitionCallback callback); + virtual void RemoveZoneTransitionUpdates(ZoneTransitionCallback callback); /** * Adds callback for updates of device motion events. */ - virtual void requestDeviceMotionUpdates(DeviceMotionCallback callback); + virtual void RequestDeviceMotionUpdates(DeviceMotionCallback callback); /** * Remove callback for updates of device motion events. */ - virtual void removeDeviceMotionUpdates(DeviceMotionCallback callback); + virtual void RemoveDeviceMotionUpdates(DeviceMotionCallback callback); /** * Returns the best ranging estimate to a given device. Returns {@code @@ -143,7 +143,7 @@ class SensorFusion { * * @param device_id Id of the peer device. */ - virtual std::optional getRangingData(uint64_t device_id); + virtual std::optional GetRangingData(uint64_t device_id); }; } // namespace presence From 7e1f286d93c7b1f1e41649f68afedababea85308 Mon Sep 17 00:00:00 2001 From: Thomas Van Lenten Date: Wed, 26 Jul 2023 14:24:17 -0700 Subject: [PATCH 019/128] Don't compile `utils.mm` twice. PiperOrigin-RevId: 551315072 --- internal/platform/implementation/apple/BUILD | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index a43eb67c..b2dafb1f 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -32,7 +32,6 @@ objc_library( "preferences_manager.mm", "scheduled_executor.mm", "timer.mm", - "utils.mm", "wifi_lan.mm", ], hdrs = [ @@ -44,7 +43,6 @@ objc_library( "scheduled_executor.h", "single_thread_executor.h", "timer.h", - "utils.h", "wifi_lan.h", ], # Prevent Objective-C++ headers from being pulled into swift. From 040ed31341f03ded50fa97ef3b075d645f07ecf7 Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Wed, 26 Jul 2023 14:28:54 -0700 Subject: [PATCH 020/128] Integrate Dart UX with windows_admin_plugin PiperOrigin-RevId: 551316220 --- fastpair/keyed_service/fast_pair_mediator.cc | 2 +- fastpair/plugins/windows_admin_plugin.cc | 56 +++++++++++++++++--- fastpair/plugins/windows_admin_plugin.h | 2 + 3 files changed, 51 insertions(+), 9 deletions(-) diff --git a/fastpair/keyed_service/fast_pair_mediator.cc b/fastpair/keyed_service/fast_pair_mediator.cc index a58c6986..16850e2c 100644 --- a/fastpair/keyed_service/fast_pair_mediator.cc +++ b/fastpair/keyed_service/fast_pair_mediator.cc @@ -130,7 +130,7 @@ void Mediator::OnDiscoveryAction(FastPairDevice& device, NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByUser"; foreground_currently_showing_notification_ = false; // TODO(b/285453663): update discovery block list - [[fallthrough]]; + break; case DiscoveryAction::kDismissedByTimeout: NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByTimeout"; foreground_currently_showing_notification_ = false; diff --git a/fastpair/plugins/windows_admin_plugin.cc b/fastpair/plugins/windows_admin_plugin.cc index 99430c11..c78f4b74 100644 --- a/fastpair/plugins/windows_admin_plugin.cc +++ b/fastpair/plugins/windows_admin_plugin.cc @@ -22,13 +22,45 @@ namespace fastpair { void WindowsAdminPlugin::PluginState::DiscoveryClicked(DiscoveryAction action) { NEARBY_LOGS(INFO) << __func__; if (device == nullptr || fast_pair_service == nullptr) return; - absl::Status status = fast_pair_service->GetSeeker()->StartInitialPairing( - *device, InitialPairingParam{}, - {.on_pairing_result = [](const FastPairDevice& device, - absl::Status status) { - NEARBY_LOGS(INFO) << "Pairing result: " << status; - }}); - NEARBY_LOGS(INFO) << "StartInitialPairing: " << status; + switch (action) { + case DiscoveryAction::kPairToDevice: { + NEARBY_LOGS(INFO) << __func__ << ": Action = kPairToDevice"; + absl::Status status = fast_pair_service->GetSeeker()->StartInitialPairing( + *device, InitialPairingParam{}, + {.on_pairing_result = [this](const FastPairDevice& device, + absl::Status status) { + NEARBY_LOGS(INFO) << "Pairing result: " << status; + for (auto* observer : observers.GetObservers()) { + observer->OnPairingResult(device.GetMetadata().value(), + status.ok()); + } + }}); + NEARBY_LOGS(INFO) << "StartInitialPairing: " << status; + } break; + case DiscoveryAction::kDismissedByOs: + NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByOs"; + break; + case DiscoveryAction::kDismissedByUser: + // When the user explicitly dismisses the discovery notification, update + // the device's block-list value accordingly. + NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByUser"; + foreground_currently_showing_notification = false; + break; + case DiscoveryAction::kDismissedByTimeout: + NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByTimeout"; + foreground_currently_showing_notification = false; + break; + case DiscoveryAction::kLearnMore: + NEARBY_LOGS(INFO) << __func__ << ": Action = kLearnMore"; + break; + case DiscoveryAction::kDone: + NEARBY_LOGS(INFO) << __func__ << ": Action = kDone"; + foreground_currently_showing_notification = false; + break; + default: + NEARBY_LOGS(INFO) << __func__ << ": Action = Unknown"; + break; + } } void WindowsAdminPlugin::PluginState::SetIsScreenLocked(bool is_locked) { @@ -48,6 +80,14 @@ void WindowsAdminPlugin::OnInitialDiscoveryEvent( << "Ignoring initial discovery event because metadata is missing"; return; } + if (state_->foreground_currently_showing_notification) { + NEARBY_LOGS(VERBOSE) << __func__ + << ": Already showing a notification for a device"; + return; + } + // Show discovery notification + state_->foreground_currently_showing_notification = true; + state_->device = device_; for (auto* observer : state_->observers.GetObservers()) { observer->OnUpdateDevice(*metadata); @@ -61,7 +101,7 @@ void WindowsAdminPlugin::OnPairEvent(const PairEvent& event) { {.on_pairing_result = [](const FastPairDevice& device, absl::Status status) { NEARBY_LOGS(INFO) << "Pairing result: " << status; - // TODO(jsobczak): Ask for user constent and save the Account Key to + // TODO(jsobczak): Ask for user consent and save the Account Key to // user's account. }}); NEARBY_LOGS(INFO) << "StartRetroactivePairing: " << status; diff --git a/fastpair/plugins/windows_admin_plugin.h b/fastpair/plugins/windows_admin_plugin.h index 5d2ce3d3..6dd600cf 100644 --- a/fastpair/plugins/windows_admin_plugin.h +++ b/fastpair/plugins/windows_admin_plugin.h @@ -37,7 +37,9 @@ class WindowsAdminPlugin : public FastPairPlugin { ObserverList observers; const FastPairDevice* device = nullptr; std::unique_ptr fast_pair_service; + bool foreground_currently_showing_notification = false; }; + class Provider : public FastPairPluginProvider { public: explicit Provider(PluginState* state) : state_(state) {} From 2145ccce6916a865360863f29dc9d0bc8d1d713c Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Wed, 26 Jul 2023 15:14:25 -0700 Subject: [PATCH 021/128] Fix flaky test:scanner_broker_impl_test PiperOrigin-RevId: 551329018 --- fastpair/scanning/BUILD | 1 + .../fast_pair_discoverable_scanner.cc | 13 ++- .../fastpair/fast_pair_discoverable_scanner.h | 5 +- .../fast_pair_discoverable_scanner_test.cc | 2 - .../fast_pair_non_discoverable_scanner.cc | 13 ++- .../fast_pair_non_discoverable_scanner.h | 4 + fastpair/scanning/scanner_broker_impl_test.cc | 86 +++++++++++++------ 7 files changed, 88 insertions(+), 36 deletions(-) diff --git a/fastpair/scanning/BUILD b/fastpair/scanning/BUILD index 521dd002..3478b93e 100644 --- a/fastpair/scanning/BUILD +++ b/fastpair/scanning/BUILD @@ -68,6 +68,7 @@ cc_test( "//fastpair/common", "//fastpair/internal/mediums", "//fastpair/proto:fastpair_cc_proto", + "//fastpair/repository:device_repository", "//fastpair/repository:test_support", "//fastpair/testing", "//internal/platform:base", diff --git a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner.cc b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner.cc index e44f5172..5206bf93 100644 --- a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner.cc +++ b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner.cc @@ -203,20 +203,27 @@ void FastPairDiscoverableScanner::NotifyDeviceFound(FastPairDevice& device) { NEARBY_LOGS(VERBOSE) << "Notify Device found:" << "BluetoothAddress = " << device.GetBleAddress() << ", Model id = " << device.GetModelId(); + { + MutexLock lock(&mutex_); + notified_devices_[device.GetBleAddress()] = &device; + } found_callback_(device); } void FastPairDiscoverableScanner::OnDeviceLost( const BlePeripheral& peripheral) { NEARBY_LOGS(INFO) << __func__ << ": Running lost callback"; + { + MutexLock lock(&mutex_); + auto node = notified_devices_.extract(peripheral.GetName()); + // Don't invoke callback if we didn't notify this device. + if (node.empty()) return; + } executor_->Execute("device-lost", [this, address = peripheral.GetName()]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { auto opt_device = device_repository_->FindDevice(address); - - // Don't invoke callback if we didn't notify this - // device. if (!opt_device.has_value()) return; FastPairDevice* device = opt_device.value(); lost_callback_(*device); diff --git a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner.h b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner.h index c53243e0..69606136 100644 --- a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner.h +++ b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner.h @@ -26,7 +26,7 @@ #include "fastpair/scanning/fastpair/fast_pair_scanner.h" #include "internal/base/observer_list.h" #include "internal/platform/bluetooth_adapter.h" -#include "internal/platform/logging.h" +#include "internal/platform/mutex.h" #include "internal/platform/single_thread_executor.h" namespace nearby { @@ -84,10 +84,13 @@ class FastPairDiscoverableScanner : public FastPairScanner::Observer { void NotifyDeviceFound(FastPairDevice& device) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + Mutex mutex_; FastPairScanner& scanner_; DiscoverableScannerCallback found_callback_ ABSL_GUARDED_BY(*executor_); DiscoverableScannerCallback lost_callback_ ABSL_GUARDED_BY(*executor_); SingleThreadExecutor* executor_; + absl::flat_hash_map notified_devices_ + ABSL_GUARDED_BY(mutex_); FastPairDeviceRepository* device_repository_ ABSL_GUARDED_BY(*executor_); ObserverList observer_list_; }; diff --git a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_test.cc b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_test.cc index cc639a10..f487e463 100644 --- a/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_test.cc +++ b/fastpair/scanning/fastpair/fast_pair_discoverable_scanner_test.cc @@ -309,8 +309,6 @@ TEST_F(FastPairDiscoverableScannerTest, std::make_unique(kTestBleDeviceAddress, kValidModelId); scanner_->NotifyDeviceLost(BlePeripheral(ble_peripheral.get())); EXPECT_FALSE(lost_notification.WaitForNotificationWithTimeout(kWaitTimeout)); - scanner_->NotifyDeviceLost(BlePeripheral(ble_peripheral.get())); - EXPECT_FALSE(lost_notification.WaitForNotificationWithTimeout(kWaitTimeout)); } } // namespace diff --git a/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.cc b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.cc index 43148584..009ab87e 100644 --- a/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.cc +++ b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.cc @@ -169,20 +169,27 @@ void FastPairNonDiscoverableScanner::NotifyDeviceFound(FastPairDevice& device) { NEARBY_LOGS(VERBOSE) << "Notify Device found:" << "BluetoothAddress = " << device.GetBleAddress() << ", Model id = " << device.GetModelId(); + { + MutexLock lock(&mutex_); + notified_devices_[device.GetBleAddress()] = &device; + } found_callback_(device); } void FastPairNonDiscoverableScanner::OnDeviceLost( const BlePeripheral& peripheral) { NEARBY_LOGS(INFO) << __func__ << ": Running lost callback"; + { + MutexLock lock(&mutex_); + auto node = notified_devices_.extract(peripheral.GetName()); + // Don't invoke callback if we didn't notify this device. + if (node.empty()) return; + } executor_->Execute("device-lost", [this, address = peripheral.GetName()]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { auto opt_device = device_repository_->FindDevice(address); - - // Don't invoke callback if we didn't notify this - // device. if (!opt_device.has_value()) return; FastPairDevice* device = opt_device.value(); lost_callback_(*device); diff --git a/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h index aa38e825..1db0c2cc 100644 --- a/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h +++ b/fastpair/scanning/fastpair/fast_pair_non_discoverable_scanner.h @@ -24,6 +24,7 @@ #include "fastpair/repository/fast_pair_device_repository.h" #include "fastpair/scanning/fastpair/fast_pair_scanner.h" #include "internal/base/observer_list.h" +#include "internal/platform/mutex.h" #include "internal/platform/single_thread_executor.h" namespace nearby { @@ -90,10 +91,13 @@ class FastPairNonDiscoverableScanner : public FastPairScanner::Observer { void NotifyDeviceFound(FastPairDevice& device) ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); + Mutex mutex_; FastPairScanner& scanner_; NonDiscoverableScannerCallback found_callback_ ABSL_GUARDED_BY(*executor_); NonDiscoverableScannerCallback lost_callback_ ABSL_GUARDED_BY(*executor_); SingleThreadExecutor* executor_; + absl::flat_hash_map notified_devices_ + ABSL_GUARDED_BY(mutex_); FastPairDeviceRepository* device_repository_ ABSL_GUARDED_BY(*executor_); ObserverList observer_list_; }; diff --git a/fastpair/scanning/scanner_broker_impl_test.cc b/fastpair/scanning/scanner_broker_impl_test.cc index 58f3eb1c..6e00d9a4 100644 --- a/fastpair/scanning/scanner_broker_impl_test.cc +++ b/fastpair/scanning/scanner_broker_impl_test.cc @@ -26,16 +26,17 @@ #include "fastpair/internal/mediums/mediums.h" #include "fastpair/proto/fastpair_rpcs.proto.h" #include "fastpair/repository/fake_fast_pair_repository.h" +#include "fastpair/repository/fast_pair_device_repository.h" #include "fastpair/scanning/scanner_broker.h" #include "fastpair/testing/fast_pair_service_data_creator.h" #include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" +#include "internal/platform/single_thread_executor.h" namespace nearby { namespace fastpair { namespace { -constexpr absl::Duration kTaskWaitTimeout = absl::Milliseconds(1000); constexpr int kNotDiscoverableAdvHeader = 0b00000110; constexpr int kAccountKeyFilterHeader = 0b01100000; constexpr int kSaltHeader = 0b00010001; @@ -70,38 +71,62 @@ class ScannerBrokerObserver : public ScannerBroker::Observer { CountDownLatch* lost_latch_ = nullptr; }; +class MediumEnvironmentStarter { + public: + MediumEnvironmentStarter() { MediumEnvironment::Instance().Start(); } + ~MediumEnvironmentStarter() { MediumEnvironment::Instance().Stop(); } +}; + class ScannerBrokerImplTest : public testing::Test { protected: - MediumEnvironment& env_{MediumEnvironment::Instance()}; + void SetUp() override { + MediumEnvironment::Instance().Start(); + advertiser_ble_address_ = + mediums_advertiser_.GetBle().GetMedium().GetAdapter().GetMacAddress(); + } + + void TearDown() override { MediumEnvironment::Instance().Stop(); } + + // The medium environment must be initialized (started) + // before registering medium. + MediumEnvironmentStarter env_; + Mediums mediums_scanner_; + Mediums mediums_advertiser_; + std::string advertiser_ble_address_; }; TEST_F(ScannerBrokerImplTest, FoundDiscoverableAdvertisement) { - env_.Start(); + SingleThreadExecutor executor; + FastPairDeviceRepository devices{&executor}; + // Setup FakeFastPairRepository std::string decoded_key; absl::Base64Unescape(kPublicAntiSpoof, &decoded_key); - SingleThreadExecutor executor; - FastPairDeviceRepository devices(&executor); proto::Device metadata; auto repository_ = std::make_unique(); metadata.mutable_anti_spoofing_key_pair()->set_public_key(decoded_key); repository_->SetFakeMetadata(kModelId, metadata); - // Create Fast Pair Scanner and add its observer - Mediums mediums_1; - auto scanner_broker = - std::make_unique(mediums_1, &executor, &devices); + // Create Scanner and ScannerBrokerObserver + auto scanner_broker = std::make_unique( + mediums_scanner_, &executor, &devices); CountDownLatch accept_latch(1); CountDownLatch lost_latch(1); + CountDownLatch device_removed(1); + FastPairDeviceRepository::RemoveDeviceCallback callback = + [&](const FastPairDevice& device) { + EXPECT_EQ(device.GetBleAddress(), advertiser_ble_address_); + device_removed.CountDown(); + }; + devices.AddObserver(&callback); ScannerBrokerObserver observer(scanner_broker.get(), &accept_latch, &lost_latch); // Create Advertiser and startAdvertising - Mediums mediums_2; std::string service_id(kServiceID); ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; std::string fast_pair_service_uuid(kFastPairServiceUuid); - mediums_2.GetBle().GetMedium().StartAdvertising( + mediums_advertiser_.GetBle().GetMedium().StartAdvertising( service_id, advertisement_bytes, fast_pair_service_uuid); // Fast Pair scanner startScanning @@ -109,38 +134,44 @@ TEST_F(ScannerBrokerImplTest, FoundDiscoverableAdvertisement) { scanner_broker->StartScanning(Protocol::kFastPairInitialPairing); // Notify device found - EXPECT_TRUE(accept_latch.Await(kTaskWaitTimeout).result()); + accept_latch.Await(); // Advertiser stopAdvertising - mediums_2.GetBle().GetMedium().StopAdvertising(service_id); + mediums_advertiser_.GetBle().GetMedium().StopAdvertising(service_id); // Notify device lost - EXPECT_TRUE(lost_latch.Await(kTaskWaitTimeout).result()); + lost_latch.Await(); + device_removed.Await(); scanning_session.reset(); - env_.Stop(); } TEST_F(ScannerBrokerImplTest, FoundNonDiscoverableAdvertisement) { - env_.Start(); SingleThreadExecutor executor; - FastPairDeviceRepository devices(&executor); + FastPairDeviceRepository devices{&executor}; + + // Setup FakeFastPairRepository auto repository = std::make_unique(); proto::Device metadata; repository->SetFakeMetadata(kModelId, metadata); repository->SetResultOfCheckIfAssociatedWithCurrentAccount(AccountKey(), kModelId); - // Create Fast Pair Scanner and add its observer - Mediums mediums_1; - auto scanner_broker = - std::make_unique(mediums_1, &executor, &devices); + // Create Scanner and ScannerBrokerObserver + auto scanner_broker = std::make_unique( + mediums_scanner_, &executor, &devices); CountDownLatch accept_latch(1); CountDownLatch lost_latch(1); + CountDownLatch device_removed(1); + FastPairDeviceRepository::RemoveDeviceCallback callback = + [&](const FastPairDevice& device) { + EXPECT_EQ(device.GetBleAddress(), advertiser_ble_address_); + device_removed.CountDown(); + }; + devices.AddObserver(&callback); ScannerBrokerObserver observer(scanner_broker.get(), &accept_latch, &lost_latch); // Create Advertiser and startAdvertising - Mediums mediums_2; std::string service_id(kServiceID); std::vector service_data = FastPairServiceDataCreator::Builder() @@ -155,7 +186,7 @@ TEST_F(ScannerBrokerImplTest, FoundNonDiscoverableAdvertisement) { ByteArray advertisement_bytes( std::string(service_data.begin(), service_data.end())); std::string fast_pair_service_uuid(kFastPairServiceUuid); - mediums_2.GetBle().GetMedium().StartAdvertising( + mediums_advertiser_.GetBle().GetMedium().StartAdvertising( service_id, advertisement_bytes, fast_pair_service_uuid); // Fast Pair scanner startScanning @@ -163,16 +194,17 @@ TEST_F(ScannerBrokerImplTest, FoundNonDiscoverableAdvertisement) { scanner_broker->StartScanning(Protocol::kFastPairInitialPairing); // Notify device found - EXPECT_TRUE(accept_latch.Await(kTaskWaitTimeout).result()); + accept_latch.Await(); // Advertiser stopAdvertising - mediums_2.GetBle().GetMedium().StopAdvertising(service_id); + mediums_advertiser_.GetBle().GetMedium().StopAdvertising(service_id); // Notify device lost - EXPECT_TRUE(lost_latch.Await(kTaskWaitTimeout).result()); + lost_latch.Await(); + device_removed.Await(); scanning_session.reset(); - env_.Stop(); } + } // namespace } // namespace fastpair } // namespace nearby From a42cdc7346db66885199e2bc764703987ae3db2f Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Wed, 26 Jul 2023 15:20:50 -0700 Subject: [PATCH 022/128] Move Server Socket and its dependencies to their own files PiperOrigin-RevId: 551330682 --- Package.swift | 5 + internal/platform/implementation/apple/BUILD | 30 +++ .../implementation/apple/ble_peripheral.h | 54 +++++ .../implementation/apple/ble_peripheral.mm | 38 ++++ .../implementation/apple/ble_server_socket.h | 76 +++++++ .../implementation/apple/ble_server_socket.mm | 82 +++++++ .../implementation/apple/ble_socket.h | 130 +++++++++++ .../implementation/apple/ble_socket.mm | 210 ++++++++++++++++++ 8 files changed, 625 insertions(+) create mode 100644 internal/platform/implementation/apple/ble_peripheral.h create mode 100644 internal/platform/implementation/apple/ble_peripheral.mm create mode 100644 internal/platform/implementation/apple/ble_server_socket.h create mode 100644 internal/platform/implementation/apple/ble_server_socket.mm create mode 100644 internal/platform/implementation/apple/ble_socket.h create mode 100644 internal/platform/implementation/apple/ble_socket.mm diff --git a/Package.swift b/Package.swift index e9ceacdc..e4c984bd 100644 --- a/Package.swift +++ b/Package.swift @@ -570,6 +570,11 @@ let package = Package( "connections/implementation/mediums/webrtc", // This breaks the build, but seems to work fine without it? "internal/platform/medium_environment.cc", + // Temporarily ignore BLEv2 source files. + // TODO(b/293283024): Stop ignoring these files when BLEv2 migration is complete. + "internal/platform/implementation/apple/ble_peripheral.mm", + "internal/platform/implementation/apple/ble_server_socket.mm", + "internal/platform/implementation/apple/ble_socket.mm", ], sources: [ "compiled_proto", diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index b2dafb1f..af77fa20 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -104,6 +104,36 @@ objc_library( ], ) +objc_library( + name = "ble_v2", + srcs = [ + "ble_peripheral.mm", + "ble_server_socket.mm", + "ble_socket.mm", + "ble_utils.mm", + "utils.mm", + ], + hdrs = [ + "ble_peripheral.h", + "ble_server_socket.h", + "ble_socket.h", + "ble_utils.h", + "utils.h", + ], + # Prevent Objective-C++ headers from being pulled into swift. + aspect_hints = ["//tools/build_defs/swift:no_module"], + deps = [ + "//internal/platform:base", + "//internal/platform/implementation:comm", + "//internal/platform/implementation/apple/Mediums", + "//third_party/apple_frameworks:CoreBluetooth", + "//third_party/apple_frameworks:Foundation", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/strings", + ], +) + objc_library( name = "Shared", srcs = [ diff --git a/internal/platform/implementation/apple/ble_peripheral.h b/internal/platform/implementation/apple/ble_peripheral.h new file mode 100644 index 00000000..b31b918e --- /dev/null +++ b/internal/platform/implementation/apple/ble_peripheral.h @@ -0,0 +1,54 @@ +// 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. + +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data + +#import +#import + +#include + +#include "internal/platform/implementation/ble_v2.h" + +namespace nearby { +namespace apple { + +// Opaque wrapper over a CoreBluetooth peripheral. This can be used to uniquely +// identify a peripheral and connect to its GATT server. +class BlePeripheral : public api::ble_v2::BlePeripheral { + public: + explicit BlePeripheral(CBPeripheral *peripheral); + ~BlePeripheral() override = default; + + // Returns the hardware address of this peripheral. + // + // For example, "00:11:22:AA:BB:CC". + std::string GetAddress() const override; + + // Returns an immutable unique identifier. The identifier does not change when + // the peripheral's address is rotated. + api::ble_v2::BlePeripheral::UniqueId GetUniqueId() const override; + + private: + CBPeripheral *peripheral_; + api::ble_v2::BlePeripheral::UniqueId unique_id_; +}; + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_peripheral.mm b/internal/platform/implementation/apple/ble_peripheral.mm new file mode 100644 index 00000000..8dedbd4e --- /dev/null +++ b/internal/platform/implementation/apple/ble_peripheral.mm @@ -0,0 +1,38 @@ +// 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/ble_peripheral.h" + +#import +#import + +#include + +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/prng.h" + +namespace nearby { +namespace apple { + +BlePeripheral::BlePeripheral(CBPeripheral *peripheral) + : peripheral_(peripheral), unique_id_(Prng().NextInt64()) {} + +std::string BlePeripheral::GetAddress() const { + return peripheral_.identifier.UUIDString.UTF8String; +} + +api::ble_v2::BlePeripheral::UniqueId BlePeripheral::GetUniqueId() const { return unique_id_; } + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_server_socket.h b/internal/platform/implementation/apple/ble_server_socket.h new file mode 100644 index 00000000..f4758e0c --- /dev/null +++ b/internal/platform/implementation/apple/ble_server_socket.h @@ -0,0 +1,76 @@ +// 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. + +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data + +// TODO(b/293336684): Remove this file when shared Weave is complete. + +#import + +#include + +#include "absl/functional/any_invocable.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/ble_v2.h" + +#import "internal/platform/implementation/apple/ble_socket.h" + +namespace nearby { +namespace apple { + +// A BLE server socket for listening for incoming Weave sockets. +class BleServerSocket : public api::ble_v2::BleServerSocket { + public: + BleServerSocket() = default; + ~BleServerSocket() override; + + // Wait for an available socket. + // + // Blocks until either: + // - at least one incoming connection request is available, or + // - ServerSocket is closed. + // + // On success, returns connected socket, ready to exchange data or nullptr on + // error. Once error is reported, it is permanent, and ServerSocket must be + // closed. + std::unique_ptr Accept() override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Close the server socket. + // + // Returns Exception::kIo on error, otherwise Exception::kSuccess. + Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + + bool Connect(std::unique_ptr socket) ABSL_LOCKS_EXCLUDED(mutex_); + void SetCloseNotifier(absl::AnyInvocable notifier) + ABSL_LOCKS_EXCLUDED(mutex_); + + private: + Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + mutable absl::Mutex mutex_; + absl::CondVar cond_; + absl::flat_hash_set> pending_sockets_ + ABSL_GUARDED_BY(mutex_); + absl::AnyInvocable close_notifier_ ABSL_GUARDED_BY(mutex_); + bool closed_ ABSL_GUARDED_BY(mutex_) = false; +}; + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_server_socket.mm b/internal/platform/implementation/apple/ble_server_socket.mm new file mode 100644 index 00000000..42c2d1ef --- /dev/null +++ b/internal/platform/implementation/apple/ble_server_socket.mm @@ -0,0 +1,82 @@ +// 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. + +// TODO(b/293336684): Remove this file when shared Weave is complete. + +#import "internal/platform/implementation/apple/ble_server_socket.h" + +#import + +#include +#include + +namespace nearby { +namespace apple { + +BleServerSocket::~BleServerSocket() { + absl::MutexLock lock(&mutex_); + DoClose(); +} + +std::unique_ptr BleServerSocket::Accept() { + absl::MutexLock lock(&mutex_); + while (!closed_ && pending_sockets_.empty()) { + cond_.Wait(&mutex_); + } + if (closed_) return {}; + + std::unique_ptr remote_socket = + std::move(pending_sockets_.extract(pending_sockets_.begin()).value()); + return std::move(remote_socket); +} + +bool BleServerSocket::Connect(std::unique_ptr socket) { + absl::MutexLock lock(&mutex_); + if (closed_) { + return false; + } + pending_sockets_.insert(std::move(socket)); + cond_.SignalAll(); + return !closed_; +} + +void BleServerSocket::SetCloseNotifier(absl::AnyInvocable notifier) { + absl::MutexLock lock(&mutex_); + close_notifier_ = std::move(notifier); +} + +Exception BleServerSocket::Close() { + absl::MutexLock lock(&mutex_); + return DoClose(); +} + +Exception BleServerSocket::DoClose() { + bool should_notify = !closed_; + closed_ = true; + if (should_notify) { + cond_.SignalAll(); + if (close_notifier_) { + auto notifier = std::move(close_notifier_); + mutex_.Unlock(); + // Notifier may contain calls to public API, and may cause deadlock, if + // mutex_ is held during the call. + notifier(); + mutex_.Lock(); + } + } + return {Exception::kSuccess}; +} + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_socket.h b/internal/platform/implementation/apple/ble_socket.h new file mode 100644 index 00000000..0e651719 --- /dev/null +++ b/internal/platform/implementation/apple/ble_socket.h @@ -0,0 +1,130 @@ +// 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. + +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data + +// TODO(b/293336684): Remove this file when shared Weave is complete. + +#import + +#include + +#include "internal/platform/implementation/ble_v2.h" + +#import "internal/platform/implementation/apple/ble_peripheral.h" + +@class GNCMConnectionHandlers; +@protocol GNCMConnection; + +namespace nearby { +namespace apple { + +// A readable stream of bytes. +class BleInputStream : public InputStream { + public: + BleInputStream(); + ~BleInputStream() override; + + // Reads at most `size` bytes from the input stream. + // + // Returns an empty byte array on end of file, or Exception::kIo on error. + ExceptionOr Read(std::int64_t size) override; + + // Closes the stream preventing further reads. + // + // Returns Exception::kIo on error, otherwise Exception::kSuccess. + Exception Close() override; + + GNCMConnectionHandlers *GetConnectionHandlers() { return connectionHandlers_; } + + private: + GNCMConnectionHandlers *connectionHandlers_; + NSMutableArray *newDataPackets_; + NSMutableData *accumulatedData_; + NSCondition *condition_; +}; + +// A writable stream of bytes. +class BleOutputStream : public OutputStream { + public: + explicit BleOutputStream(id connection) + : connection_(connection), condition_([[NSCondition alloc] init]) {} + ~BleOutputStream() override; + + // Write the provided bytes to the output stream. + // + // Returns Exception::kIo on error, otherwise Exception::kSuccess. + Exception Write(const ByteArray &data) override; + + // no-op + // + // Always returns Exception::kSuccess. + Exception Flush() override; + + // Closes the stream preventing further writes. + // + // Returns Exception::kIo on error, otherwise Exception::kSuccess. + Exception Close() override; + + private: + id connection_; + NSCondition *condition_; +}; + +// A BLE Weave socket. +class BleSocket : public api::ble_v2::BleSocket { + public: + BleSocket(id connection, BlePeripheral *peripheral); + ~BleSocket() override; + + // Returns the InputStream of the BleSocket. + // On error, returned stream will report Exception::kIo on any operation. + // + // The returned object is not owned by the caller, and can be invalidated once + // the BleSocket object is destroyed. + InputStream &GetInputStream() override { return *input_stream_; } + + // Returns the OutputStream of the BleSocket. + // On error, returned stream will report Exception::kIo on any operation. + // + // The returned object is not owned by the caller, and can be invalidated once + // the BleSocket object is destroyed. + OutputStream &GetOutputStream() override { return *output_stream_; } + + // Returns Exception::kIo on error, otherwise Exception::kSuccess. + Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns valid BlePeripheral pointer if there is a connection, and + // nullptr otherwise. + BlePeripheral *GetRemotePeripheral() override { return peripheral_; } + + bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); + + private: + void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + mutable absl::Mutex mutex_; + bool closed_ ABSL_GUARDED_BY(mutex_) = false; + std::unique_ptr input_stream_; + std::unique_ptr output_stream_; + BlePeripheral *peripheral_; +}; + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_socket.mm b/internal/platform/implementation/apple/ble_socket.mm new file mode 100644 index 00000000..a868ff0b --- /dev/null +++ b/internal/platform/implementation/apple/ble_socket.mm @@ -0,0 +1,210 @@ +// 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/ble_socket.h" + +#include "internal/platform/implementation/ble_v2.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" + +// TODO(b/293336684): Remove this file when shared Weave is complete. + +namespace nearby { +namespace apple { + +#pragma mark - BleInputStream + +BleInputStream::BleInputStream() + : newDataPackets_([NSMutableArray array]), + accumulatedData_([NSMutableData data]), + condition_([[NSCondition alloc] init]) { + // Create the handlers of incoming data from the remote endpoint. + connectionHandlers_ = [GNCMConnectionHandlers + payloadHandler:^(NSData *data) { + [condition_ lock]; + // Add the incoming data to the data packet array to be processed in read() below. + [newDataPackets_ addObject:data]; + [condition_ broadcast]; + [condition_ unlock]; + } + disconnectedHandler:^{ + [condition_ lock]; + // Release the data packet array, meaning the stream has been closed or severed. + newDataPackets_ = nil; + [condition_ broadcast]; + [condition_ unlock]; + }]; +} + +BleInputStream::~BleInputStream() { + NSCAssert(!newDataPackets_, @"BleInputStream not closed before destruction"); +} + +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 (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]; + } + + 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) { + NSLog(@"[NEARBY] Input stream: Received data of size: %lu", (unsigned long)dataToReturn.length); + return ExceptionOr(ByteArrayFromNSData(dataToReturn)); + } else { + return ExceptionOr{Exception::kIo}; + } +} + +Exception BleInputStream::Close() { + // Unblock pending read operation. + [condition_ lock]; + newDataPackets_ = nil; + [condition_ broadcast]; + [condition_ unlock]; + return {Exception::kSuccess}; +} + +#pragma mark - BleOutputStream + +BleOutputStream::~BleOutputStream() { + NSCAssert(!connection_, @"BleOutputStream not closed before destruction"); +} + +Exception BleOutputStream::Write(const ByteArray &data) { + [condition_ lock]; + NSLog(@"[NEARBY] Sending data of size: %lu", NSDataFromByteArray(data).length); + + if (!connection_) { + [condition_ unlock]; + return {Exception::kIo}; + } + + NSMutableData *packet = [NSMutableData dataWithData:NSDataFromByteArray(data)]; + + // Send the data, blocking until the completion handler is called. + __block bool isComplete = NO; + __block GNCMPayloadResult sendResult = GNCMPayloadResultFailure; + NSCondition *condition = condition_; // don't capture |this| in completion + + [connection_ sendData:packet + progressHandler:^(size_t count) { + } + completion:^(GNCMPayloadResult result) { + [condition lock]; + // Make sure we haven't already reported completion before. This prevents a crash + // where we try leaving a dispatch group more times than we entered it. + // b/79095653. + if (isComplete) { + [condition unlock]; + return; + } + isComplete = YES; + sendResult = result; + [condition broadcast]; + [condition unlock]; + }]; + + while (connection_ && !isComplete) { + [condition_ wait]; + } + + if (sendResult == GNCMPayloadResultSuccess) { + [condition_ unlock]; + return {Exception::kSuccess}; + } else { + [condition_ unlock]; + return {Exception::kIo}; + } +} + +Exception BleOutputStream::Flush() { + // The write() function blocks until the data is received by the remote endpoint, so there's + // nothing to do here. + return {Exception::kSuccess}; +} + +Exception BleOutputStream::Close() { + // Unblock pending write operation. + [condition_ lock]; + connection_ = nil; + [condition_ broadcast]; + [condition_ unlock]; + return {Exception::kSuccess}; +} + +#pragma mark - BleSocket + +BleSocket::BleSocket(id connection, BlePeripheral *peripheral) + : input_stream_(new BleInputStream()), + output_stream_(new BleOutputStream(connection)), + peripheral_(peripheral) {} + +BleSocket::~BleSocket() { + absl::MutexLock lock(&mutex_); + DoClose(); +} + +bool BleSocket::IsClosed() const { + absl::MutexLock lock(&mutex_); + return closed_; +} + +Exception BleSocket::Close() { + absl::MutexLock lock(&mutex_); + DoClose(); + return {Exception::kSuccess}; +} + +void BleSocket::DoClose() { + if (!closed_) { + input_stream_->Close(); + output_stream_->Close(); + closed_ = true; + } +} + +} // namespace apple +} // namespace nearby From cae06bd15d1979c7dd126e37064dc638f9af59ab Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Wed, 26 Jul 2023 15:59:14 -0700 Subject: [PATCH 023/128] Close executors in BasePcpHandler::Shutdown The client can call DisconnectFromEndpointManager() explicitly before BasePcpHandler is shut down. In this case, th executors were not terminated inside BasePcpHandler::Shutdown() This caused use-after-free errors in BasePcpHandlerTest.IoError_RequestConnectionFails PiperOrigin-RevId: 551340708 --- connections/implementation/base_pcp_handler.cc | 2 +- connections/implementation/base_pcp_handler.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/connections/implementation/base_pcp_handler.cc b/connections/implementation/base_pcp_handler.cc index 38729c65..4188aa3a 100644 --- a/connections/implementation/base_pcp_handler.cc +++ b/connections/implementation/base_pcp_handler.cc @@ -84,7 +84,7 @@ BasePcpHandler::~BasePcpHandler() { } void BasePcpHandler::Shutdown() { - if (stop_) return; + if (closed_.Set(true)) return; NEARBY_LOGS(INFO) << "Initiating shutdown of BasePcpHandler(" << strategy_.GetName() << ")"; DisconnectFromEndpointManager(); diff --git a/connections/implementation/base_pcp_handler.h b/connections/implementation/base_pcp_handler.h index c149e360..de8248e9 100644 --- a/connections/implementation/base_pcp_handler.h +++ b/connections/implementation/base_pcp_handler.h @@ -609,6 +609,7 @@ class BasePcpHandler : public PcpHandler, Strategy strategy_{PcpToStrategy(pcp_)}; EncryptionRunner encryption_runner_; BwuManager* bwu_manager_; + AtomicBoolean closed_{false}; }; } // namespace connections From 35acd3d84992f0a04af16952bdbb3ec23012ff73 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Wed, 26 Jul 2023 16:35:15 -0700 Subject: [PATCH 024/128] Destroy ThroughputRecorder when PendingPayload is destroyed This fixes a race condition where a Tp Recorder was used after being destroyed. PiperOrigin-RevId: 551350023 --- connections/implementation/payload_manager.cc | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/connections/implementation/payload_manager.cc b/connections/implementation/payload_manager.cc index a01b0a64..9a423c4d 100644 --- a/connections/implementation/payload_manager.cc +++ b/connections/implementation/payload_manager.cc @@ -310,7 +310,7 @@ void PayloadManager::CancelAllPayloads() { if (pending_outgoing_payloads) { shutdown_barrier_ = - absl::make_unique(pending_outgoing_payloads); + std::make_unique(pending_outgoing_payloads); } } if (shutdown_barrier_) { @@ -457,8 +457,6 @@ void PayloadManager::SendPayload(ClientProxy* client, next_chunk_offset, resume_offset); } - ThroughputRecorderContainer::GetInstance().StopTPRecorder( - payload_id, PayloadDirection::OUTGOING_PAYLOAD); RunOnStatusUpdateThread("destroy-payload", [this, payload_id]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() { @@ -706,6 +704,10 @@ PayloadManager::PendingPayloadHandle PayloadManager::CreateIncomingPayload( void PayloadManager::OnPendingPayloadDestroy(const PendingPayload* payload) { NEARBY_LOGS(INFO) << "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", @@ -853,8 +855,6 @@ void PayloadManager::HandleFinishedIncomingPayload( const PayloadTransferFrame::PayloadHeader& payload_header, std::int64_t offset_bytes, location::nearby::proto::connections::PayloadStatus status) { - ThroughputRecorderContainer::GetInstance().StopTPRecorder( - payload_header.id(), PayloadDirection::INCOMING_PAYLOAD); SendClientCallbacksForFinishedIncomingPayload( client, endpoint_id, payload_header, offset_bytes, status); @@ -1147,9 +1147,6 @@ void PayloadManager::ProcessDataPacket( ThroughputRecorderContainer::GetInstance() .GetTPRecorder(payload_header.id(), PayloadDirection::INCOMING_PAYLOAD) ->MarkAsSuccess(); - - ThroughputRecorderContainer::GetInstance().StopTPRecorder( - payload_header.id(), PayloadDirection::INCOMING_PAYLOAD); } } From 2c1ed158bcc26c17d23c5edffdc341c3fedcbafc Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Wed, 26 Jul 2023 17:12:19 -0700 Subject: [PATCH 025/128] Add StopDiscovery call Test only change. PiperOrigin-RevId: 551358525 --- connections/implementation/p2p_cluster_pcp_handler_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/connections/implementation/p2p_cluster_pcp_handler_test.cc b/connections/implementation/p2p_cluster_pcp_handler_test.cc index 689536ce..5ca45ecf 100644 --- a/connections/implementation/p2p_cluster_pcp_handler_test.cc +++ b/connections/implementation/p2p_cluster_pcp_handler_test.cc @@ -16,6 +16,7 @@ #include #include +#include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" @@ -690,6 +691,7 @@ TEST_P(P2pClusterPcpHandlerTest, CanConnect) { EXPECT_EQ(client_a_.GetIPAddress(client_b_local_endpoint), mediums_b.GetWifi().GetInformation().ip_address_4_bytes); + handler_b.StopDiscovery(&client_b_); bwu_a.Shutdown(); bwu_b.Shutdown(); env_.Stop(); From f3bbb8caebc4def53d5b7474ba78791a9e7fd502 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Wed, 26 Jul 2023 17:27:33 -0700 Subject: [PATCH 026/128] Fix race condition in ThroughputRecorder PiperOrigin-RevId: 551361687 --- connections/implementation/analytics/throughput_recorder.cc | 5 +++++ connections/implementation/analytics/throughput_recorder.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/connections/implementation/analytics/throughput_recorder.cc b/connections/implementation/analytics/throughput_recorder.cc index 51f4c492..62d7ac21 100644 --- a/connections/implementation/analytics/throughput_recorder.cc +++ b/connections/implementation/analytics/throughput_recorder.cc @@ -138,6 +138,11 @@ bool ThroughputRecorder::Stop() { 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) { diff --git a/connections/implementation/analytics/throughput_recorder.h b/connections/implementation/analytics/throughput_recorder.h index d2bacd9c..e6dcdc05 100644 --- a/connections/implementation/analytics/throughput_recorder.h +++ b/connections/implementation/analytics/throughput_recorder.h @@ -87,7 +87,7 @@ class ThroughputRecorder { int64_t GetDurationMillis(); void OnFrameSent(Medium medium, PacketMetaData& packetMetaData); void OnFrameReceived(Medium medium, PacketMetaData& packetMetaData); - void MarkAsSuccess() { success_ = true; } + void MarkAsSuccess(); private: void CalculateDurationTimes(PacketMetaData packetMetaData); From 974d4b357ca799babd3497b7eddcb975a97fcf21 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Wed, 26 Jul 2023 18:14:47 -0700 Subject: [PATCH 027/128] Skip SDP check for FP rfcomm connection on Windows The SDP checks fails but rfcomm connection works. Skipping the SDP check does not seem to have any ill side effects. PiperOrigin-RevId: 551370531 --- fastpair/fast_pair_service.cc | 1 + internal/platform/feature_flags.h | 4 ++++ .../implementation/windows/bluetooth_classic_medium.cc | 5 ++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/fastpair/fast_pair_service.cc b/fastpair/fast_pair_service.cc index 791821b3..e511ce2a 100644 --- a/fastpair/fast_pair_service.cc +++ b/fastpair/fast_pair_service.cc @@ -43,6 +43,7 @@ namespace { constexpr char kFastPairPreferencesFilePath[] = "Google/Nearby/FastPair"; constexpr FeatureFlags::Flags fast_pair_feature_flags = FeatureFlags::Flags{ .enable_scan_for_fast_pair_advertisement = true, + .skip_service_discovery_before_connecting_to_rfcomm = true, }; constexpr absl::Duration kTimeout = absl::Seconds(3); } // namespace diff --git a/internal/platform/feature_flags.h b/internal/platform/feature_flags.h index 92d0e532..58f8835d 100644 --- a/internal/platform/feature_flags.h +++ b/internal/platform/feature_flags.h @@ -56,6 +56,10 @@ class FeatureFlags { // Controls enable or disable BLE scan advertisement for fast pair // service uuid 0x2cfe bool enable_scan_for_fast_pair_advertisement = false; + // Skip Service Discovery Protocol check if the remote party supports the + // requested service id before attempting to connect over rfcomm. SDP fails + // on Windows when connecting to FP service id but the rfcomm is successful. + bool skip_service_discovery_before_connecting_to_rfcomm = false; }; static const FeatureFlags& GetInstance() { diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.cc b/internal/platform/implementation/windows/bluetooth_classic_medium.cc index 431400ba..9daaa8d7 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.cc +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.cc @@ -311,7 +311,10 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( RfcommDeviceService requested_service( GetRequestedService(current_device, service)); - if (!CheckSdp(requested_service)) { + if (!FeatureFlags::GetInstance() + .GetFlags() + .skip_service_discovery_before_connecting_to_rfcomm && + !CheckSdp(requested_service)) { NEARBY_LOGS(ERROR) << __func__ << ": Invalid SDP."; return nullptr; } From 0a13ce51f49f2024e750c5e6f48938c87d819d7a Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Thu, 27 Jul 2023 15:39:30 -0700 Subject: [PATCH 028/128] Make `GNCBLEGATTServer` thread safe Fixes an issue where if a characteristic is created and then updated with a non-nil value, and a new characteristic under the same service is added, then when the service is re-added the old characteristic will be permanently cached and the dynamic reads will stop working. This also eliminates the issue when characteristics are created concurrently, since service additions are batched, if the service addition fails then only one of the characteristics will know that it failed to get added. This change also introduces the nuance of "success" does not mean the CoreBluetooth method has completed successfully, but instead means that the intended state has been recorded and the class will do its best to maintain that state. For example, a successful "start advertising" call means that we have the advertisement cached and we will start advertising as soon as we can (like BT transitions from off to on) and will do our best to keep advertising (like BT transitions from on to off to on again). PiperOrigin-RevId: 551660634 --- .../apple/Mediums/BLEv2/GNCBLEError.h | 26 + .../apple/Mediums/BLEv2/GNCBLEError.m | 19 + .../apple/Mediums/BLEv2/GNCBLEGATTServer.h | 43 +- .../apple/Mediums/BLEv2/GNCBLEGATTServer.m | 402 +++++++------ .../Mediums/BLEv2/GNCPeripheralManager.h | 7 + .../implementation/apple/Mediums/BUILD | 2 + .../apple/Tests/GNCBLEGATTServerTest.m | 541 +++++++++++------- .../apple/Tests/GNCFakePeripheralManager.m | 25 +- 8 files changed, 660 insertions(+), 405 deletions(-) create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.m diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h new file mode 100644 index 00000000..5eab7ec3 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h @@ -0,0 +1,26 @@ +// 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 + +/** The domain for @c NSErrors raised by the BLE medium. */ +extern NSErrorDomain const GNCBLEErrorDomain; + +/** BLE medium error codes. */ +typedef NS_ERROR_ENUM(GNCBLEErrorDomain, GNCBLEError){ + GNCBLEErrorUnknown, + GNCBLEErrorDuplicateCharacteristic, + GNCBLEErrorInvalidServiceData, + GNCBLEErrorAlreadyAdvertising, +}; diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.m new file mode 100644 index 00000000..53d2b15a --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.m @@ -0,0 +1,19 @@ +// 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/BLEv2/GNCBLEError.h" + +#import + +NSErrorDomain const GNCBLEErrorDomain = @"com.google.nearby.ble.error"; diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h index 9131555d..6408be13 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h @@ -19,11 +19,15 @@ NS_ASSUME_NONNULL_BEGIN +typedef void (^GNCCreateCharacteristicCompletionHandler)( + GNCBLEGATTCharacteristic *_Nullable characteristic, NSError *_Nullable error); +typedef void (^GNCUpdateCharacteristicCompletionHandler)(NSError *_Nullable error); +typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error); + /** * An object that manages and advertises GATT characteritics. * - * @note The public APIs of this class are NOT thread safe. All methods in this class should be - * invoked from the same thread or serially. + * @note The public APIs of this class are thread safe. */ @interface GNCBLEGATTServer : NSObject @@ -33,30 +37,31 @@ NS_ASSUME_NONNULL_BEGIN * Characteristics of the same service UUID will cause the service to be unpublished and republished * with the new characteristic appended. * - * This method blocks until the characteristic has successfully been added to the GATT server or an - * error occurs. - * * @param serviceUUID A 128-bit UUID that identifies the service that the characteristic belongs to. * @param characteristicUUID A 128-bit UUID that identifies the characteristic. * @param permissions The permissions of the characteristic value. * @param properties The properties of the characteristic. - * @return Returns the characteristic or nil if an error has occured. + * @param completionHandler Called on the main queue with the characteristic if successfully created + * or an error if one has occured. */ -- (nullable GNCBLEGATTCharacteristic *) - createCharacteristicWithServiceID:(CBUUID *)serviceUUID - characteristicUUID:(CBUUID *)characteristicUUID - permissions:(CBAttributePermissions)permissions - properties:(CBCharacteristicProperties)properties; +- (void)createCharacteristicWithServiceID:(CBUUID *)serviceUUID + characteristicUUID:(CBUUID *)characteristicUUID + permissions:(CBAttributePermissions)permissions + properties:(CBCharacteristicProperties)properties + completionHandler: + (nullable GNCCreateCharacteristicCompletionHandler)completionHandler; /** * Updates a local characteristic with the provided value. * * @param characteristic The characteristic to update. * @param value The new value for the characteristic. - * @return Returns @c YES if successfully updated or @c NO if an error has occured. + * @param completionHandler Called on the main queue with @c nil if successfully updated or an error + * if one has occured. */ -- (BOOL)updateCharacteristic:(GNCBLEGATTCharacteristic *)characteristic - value:(nullable NSData *)value; +- (void)updateCharacteristic:(GNCBLEGATTCharacteristic *)characteristic + value:(nullable NSData *)value + completionHandler:(nullable GNCUpdateCharacteristicCompletionHandler)completionHandler; /** Removes all published services from the local GATT database. */ - (void)stop; @@ -68,14 +73,14 @@ NS_ASSUME_NONNULL_BEGIN * service list is advertised using @c CBAdvertisementDataServiceUUIDsKey and the associated data is * advertised using @c CBAdvertisementDataLocalNameKey. Since @c CBAdvertisementDataLocalNameKey * does not support binary data, the value is base64 encoded and truncated if the resulting value is - * longer than 22 bytes. - * - * This method blocks until the service data is being advertised or an error occurs. + * longer than 22 bytes. This also means we can only support advertising a single service. * * @param serviceData A dictionary that contains service-specific advertisement data. - * @return Returns @c YES if successfully updated or @c NO if an error has occured. + * @param completionHandler Called on the main queue with @c nil if successfully started advertising + * or an error if one has occured. */ -- (BOOL)startAdvertisingData:(NSDictionary *)serviceData; +- (void)startAdvertisingData:(NSDictionary *)serviceData + completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler; @end diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m index e80fec94..996c8396 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m @@ -17,167 +17,186 @@ #import #import +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h" #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h" #import "internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.h" +#import "GoogleToolboxForMac/GTMLogger.h" NS_ASSUME_NONNULL_BEGIN -// An arbitrary timeout that should be pretty lenient. -static NSTimeInterval const GNCBLEGATTServerTimeoutInSeconds = 2; +static char *const kGNCBLEGATTServerQueueLabel = "com.nearby.GNCBLEGATTServer"; @interface GNCBLEGATTServer () @end @implementation GNCBLEGATTServer { + dispatch_queue_t _queue; id _peripheralManager; // A map of service UUIDs to the service. This is used to keep track of all services and - // characteristics that should be in the GATT database. This also keeps track of a characteristics - // dynamic read value. This is the value returned when a remote device attempts to read the - // characteristic. + // characteristics that have been requested to be added to the GATT database. This may not match + // the "real" services that remote devices can see. NSMutableDictionary *_services; - // A list of services that have been requested to be added to the GATT database, but have not yet - // completed. This is used by the create characteristic method to block until the characteristic - // has been added to the database. - NSMutableArray *_pendingServiceAdditions; + // A list of characteristics that will be added to the GATT database once the peripheral + // transitions into a valid state. If a characteristic is in this list it means that a + // CoreBluetooth request to add to the characteristic to the GATT database has not yet been made. + NSMutableDictionary *> + *_pendingCharacteristics; - // A map of service UUIDs to its associated error if the service had failed to be added to the - // GATT database. This is used by the create characteristic method to determine if the service was - // successfully added. - NSMutableDictionary *_serviceErrors; + // This keeps track of a characteristic's dynamic read value. This is the value returned when a + // remote device attempts to read the characteristic. + NSMutableDictionary *> *_characteristicValues; - // Guards access to @c _services, @c _pendingServiceAdditions and @c _serviceErrors. The condition - // is also used to block method execution until its async action completes. - NSCondition *_condition; + // The data that should be advertised. This value is cached until the caller explicitly stops + // advertising, because we will do our best to restart the advertisement if BT turns off then back + // on. + NSDictionary *_advertisementData; } - (instancetype)init { - dispatch_queue_t queue = - dispatch_queue_create("com.nearby.GNCBLEGATTServer", DISPATCH_QUEUE_SERIAL); - return [self initWithPeripheralManager:[[CBPeripheralManager alloc] initWithDelegate:nil - queue:queue]]; -} - -- (instancetype)initWithPeripheralManager:(id)peripheralManager { 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; +} + +// 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]; - _pendingServiceAdditions = [[NSMutableArray alloc] init]; - _serviceErrors = [[NSMutableDictionary alloc] init]; - _condition = [[NSCondition alloc] init]; + _pendingCharacteristics = [[NSMutableDictionary alloc] init]; + _characteristicValues = [[NSMutableDictionary alloc] init]; + _advertisementData = nil; } return self; -}; - -- (nullable GNCBLEGATTCharacteristic *) - createCharacteristicWithServiceID:(CBUUID *)serviceUUID - characteristicUUID:(CBUUID *)characteristicUUID - permissions:(CBAttributePermissions)permissions - properties:(CBCharacteristicProperties)properties { - // Ensure we are in a powered on state. - if (![self waitUntilPoweredOn]) { - return nil; - } - - NSDate *timeLimit = [NSDate dateWithTimeIntervalSinceNow:GNCBLEGATTServerTimeoutInSeconds]; - BOOL wasSignaled = YES; - [_condition lock]; - - // If a service with the specified UUID exists, we are modifying it, which requires us to remove - // the service and then re-add it after the new characteristic has been added. Otherwise, create - // the service for the specified UUID and keep track of it. - CBMutableService *service = _services[serviceUUID]; - if (service != nil) { - [_peripheralManager removeService:service]; - } else { - service = [[CBMutableService alloc] initWithType:serviceUUID primary:YES]; - _services[serviceUUID] = service; - } - - // Create a characteristic with the specified permissions and properties. - CBMutableCharacteristic *characteristic = - [[CBMutableCharacteristic alloc] initWithType:characteristicUUID - properties:properties - value:nil - permissions:permissions]; - - // Add the characteristic to the service's list of characteristics - NSMutableArray *characteristics = - [service.characteristics mutableCopy]; - if (characteristics == nil) { - characteristics = [[NSMutableArray alloc] init]; - } - [characteristics addObject:characteristic]; - service.characteristics = characteristics; - - // Publish the service and wait until complete. - [_pendingServiceAdditions addObject:service.UUID]; - [_peripheralManager addService:service]; - while ([_pendingServiceAdditions containsObject:service.UUID] && wasSignaled) { - wasSignaled = [_condition waitUntilDate:timeLimit]; - } - NSError *serviceError = [_serviceErrors objectForKey:service.UUID]; - [_serviceErrors removeObjectForKey:service.UUID]; - [_condition unlock]; - if (serviceError) { - return nil; - } - return [[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristicUUID - serviceUUID:serviceUUID - permissions:permissions - properties:properties]; } -- (BOOL)updateCharacteristic:(GNCBLEGATTCharacteristic *)characteristic - value:(nullable NSData *)value { - // Ensure we are in a powered on state. - if (![self waitUntilPoweredOn]) { - return NO; - } - - [_condition lock]; - // Find and update the specified characteristic's value. - CBMutableService *service = _services[characteristic.serviceUUID]; - if (!service) { - [_condition unlock]; - return NO; - } - for (CBMutableCharacteristic *c in service.characteristics) { - if ([c.UUID isEqual:characteristic.characteristicUUID]) { - c.value = value; - [_condition unlock]; - return YES; +- (void)createCharacteristicWithServiceID:(CBUUID *)serviceUUID + characteristicUUID:(CBUUID *)characteristicUUID + permissions:(CBAttributePermissions)permissions + properties:(CBCharacteristicProperties)properties + completionHandler: + (nullable GNCCreateCharacteristicCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + // Check if the characteristic exists in the pending additions or existing services. + for (CBCharacteristic *c in _pendingCharacteristics[serviceUUID]) { + if ([c.UUID isEqual:characteristicUUID]) { + if (completionHandler) { + completionHandler(nil, [NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorDuplicateCharacteristic + userInfo:nil]); + } + return; + } } - } - [_condition unlock]; - return NO; + for (CBCharacteristic *c in _services[serviceUUID].characteristics) { + if ([c.UUID isEqual:characteristicUUID]) { + if (completionHandler) { + completionHandler(nil, [NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorDuplicateCharacteristic + userInfo:nil]); + } + return; + } + } + + // Create a characteristic with the specified permissions and properties. + CBMutableCharacteristic *characteristic = + [[CBMutableCharacteristic alloc] initWithType:characteristicUUID + properties:properties + value:nil + permissions:permissions]; + // Track characteristics that need to be added to the GATT database. These will either be added + // immediately or as soon as the peripheral manager is in a valid state. + if (!_pendingCharacteristics[serviceUUID]) { + _pendingCharacteristics[serviceUUID] = [[NSMutableArray alloc] init]; + } + [_pendingCharacteristics[serviceUUID] addObject:characteristic]; + + GNCBLEGATTCharacteristic *gncCharacteristic = + [[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristicUUID + serviceUUID:serviceUUID + permissions:permissions + properties:properties]; + [self internalAddPendingServicesIfPoweredOn]; + if (completionHandler) { + completionHandler(gncCharacteristic, nil); + } + }); +} + +- (void)updateCharacteristic:(GNCBLEGATTCharacteristic *)characteristic + value:(nullable NSData *)value + completionHandler:(nullable GNCUpdateCharacteristicCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + // Keep track of the characteristics value, because we will need to retreive it during a read + // request. + if (_characteristicValues[characteristic.serviceUUID] == nil) { + _characteristicValues[characteristic.serviceUUID] = [[NSMutableDictionary alloc] init]; + } + _characteristicValues[characteristic.serviceUUID][characteristic.characteristicUUID] = value; + if (completionHandler) { + completionHandler(nil); + } + }); } - (void)stop { - [_condition lock]; - [_services removeAllObjects]; - [_condition unlock]; - [_peripheralManager removeAllServices]; + dispatch_async(_queue, ^{ + // Note: Do not clear/stop advertisements here, since there is a separate method for that. + [_peripheralManager removeAllServices]; + [_services removeAllObjects]; + [_pendingCharacteristics removeAllObjects]; + [_characteristicValues removeAllObjects]; + }); } -- (BOOL)startAdvertisingData:(NSDictionary *)serviceData { - // We can only handle advertising a single service data item, so return early if there is more - // than one service incuded. - if (serviceData.count > 1) { - return NO; - } +- (void)startAdvertisingData:(NSDictionary *)serviceData + completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + // We can only handle advertising a single service data item, so return early if there is more + // than one service incuded. + if (serviceData.count != 1) { + if (completionHandler) { + completionHandler([NSError + errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorInvalidServiceData + userInfo:@{ + NSLocalizedDescriptionKey : + @"Failed to start advertising, because Nearby for Apple platforms only " + @"supports a single service data item." + }]); + } + return; + } - // Ensure we are in a powered on state. - if (![self waitUntilPoweredOn]) { - return NO; - } + // If we have advertisement data set, that means we are already advertising and should return + // early. Advertising must be stopped before being started again. + if (_advertisementData) { + if (completionHandler) { + completionHandler([NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorAlreadyAdvertising + userInfo:nil]); + } + return; + } - if (serviceData.count == 1) { // Apple doesn't support setting service data, so we must convert it to a "local name". We do // this by assuming there will only ever be one service and then base64 encoding its associated // data. Other platforms are aware of this behavior and always check the local name if service @@ -192,107 +211,140 @@ static NSTimeInterval const GNCBLEGATTServerTimeoutInSeconds = 2; encoded = [encoded substringToIndex:22]; } - [_peripheralManager startAdvertising:@{ + _advertisementData = @{ CBAdvertisementDataLocalNameKey : encoded, CBAdvertisementDataServiceUUIDsKey : @[ serviceUUID ] - }]; - } else { - [_peripheralManager startAdvertising:nil]; - } + }; - // Wait until advertisement has started. - NSDate *timeLimit = [NSDate dateWithTimeIntervalSinceNow:GNCBLEGATTServerTimeoutInSeconds]; - BOOL wasSignaled = YES; - [_condition lock]; - while (!_peripheralManager.isAdvertising && wasSignaled) { - wasSignaled = [_condition waitUntilDate:timeLimit]; - } - [_condition unlock]; - return _peripheralManager.isAdvertising; + [self internalStartAdvertisingIfPoweredOn]; + if (completionHandler) { + completionHandler(nil); + } + }); } -#pragma mark - Helpers +#pragma mark - Internal -- (BOOL)waitUntilPoweredOn { - NSDate *timeLimit = [NSDate dateWithTimeIntervalSinceNow:GNCBLEGATTServerTimeoutInSeconds]; - BOOL timedOut = NO; - [_condition lock]; - while (_peripheralManager.state != CBManagerStatePoweredOn && !timedOut) { - timedOut = ![_condition waitUntilDate:timeLimit]; +- (void)internalAddPendingServicesIfPoweredOn { + dispatch_assert_queue(_queue); + // Services can only be added while bluetooth is on or off. Since this method will be called + // anytime the peripheral manager's state changes and turning bluetooth on/off does not reset + // the GATT database, we can add all pending services when we transition to the on or off state. + // However, other state transitions may behave differently, so we may need to modify this + // functionality in this future to re-add services if they disapear. + if (_peripheralManager.state == CBManagerStatePoweredOn || + _peripheralManager.state == CBManagerStatePoweredOff) { + for (CBUUID *serviceUUID in _pendingCharacteristics.allKeys) { + // If a service with the specified UUID exists, we are modifying it, which requires us to + // remove the service and then re-add it after the new characteristic has been added. + // Otherwise, create the service for the specified UUID and keep track of it. + CBMutableService *service = _services[serviceUUID]; + if (service != nil) { + [_peripheralManager removeService:service]; + } else { + service = [[CBMutableService alloc] initWithType:serviceUUID primary:YES]; + _services[serviceUUID] = service; + } + + // Add the pending characteristics to the current service's list of characteristics. + NSMutableArray *characteristics = + [service.characteristics mutableCopy]; + if (characteristics == nil) { + characteristics = [[NSMutableArray alloc] init]; + } + // This makes the assumption that _pendingCharacteristics does not contain any duplicate + // characteristics within itself or the current service characteristics. This is only + // enforced on characteristic creation. + [characteristics addObjectsFromArray:_pendingCharacteristics[serviceUUID]]; + service.characteristics = characteristics; + + // Publish the service. + [_peripheralManager addService:service]; + } + // Clean up pending characteristics so we don't try to re-add them any time the power state + // changes. + [_pendingCharacteristics removeAllObjects]; + } +} + +- (void)internalStartAdvertisingIfPoweredOn { + dispatch_assert_queue(_queue); + // Advertising 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 peripheral manager's state changes, so + // @c startAdvertising: will be called anytime state transitions back to powered on. + if (_peripheralManager.state == CBManagerStatePoweredOn && _advertisementData != nil) { + // Stop advertising just in case something outside of this class is advertising (like weave). + [_peripheralManager stopAdvertising]; + [_peripheralManager startAdvertising:_advertisementData]; } - [_condition unlock]; - return _peripheralManager.state == CBManagerStatePoweredOn; } #pragma mark - GNCPeripheralManagerDelegate - (void)gnc_peripheralManagerDidUpdateState:(id)peripheral { - [_condition lock]; - [_condition signal]; - [_condition unlock]; + dispatch_assert_queue(_queue); + [self internalAddPendingServicesIfPoweredOn]; + [self internalStartAdvertisingIfPoweredOn]; } - (void)gnc_peripheralManagerDidStartAdvertising:(id)peripheral error:(nullable NSError *)error { - [_condition lock]; - [_condition signal]; - [_condition unlock]; + dispatch_assert_queue(_queue); + if (error) { + GTMLoggerError(@"Failed to start advertising: %@", error); + } } - (void)gnc_peripheralManager:(id)peripheral didAddService:(CBService *)service error:(nullable NSError *)error { - [_condition lock]; - [_pendingServiceAdditions removeObject:service.UUID]; + dispatch_assert_queue(_queue); if (error) { - [_serviceErrors setObject:error forKey:service.UUID]; + GTMLoggerError(@"Failed to add service %@: %@", service, error); } - [_condition signal]; - [_condition unlock]; } - (void)gnc_peripheralManager:(id)peripheral didReceiveReadRequest:(CBATTRequest *)request { - [_condition lock]; - // Find the requested charactertic and respond with its value if it exists. - CBMutableService *service = _services[request.characteristic.service.UUID]; - if (!service) { - [_condition unlock]; + dispatch_assert_queue(_queue); + NSData *value = + _characteristicValues[request.characteristic.service.UUID][request.characteristic.UUID]; + if (!value) { [_peripheralManager respondToRequest:request withResult:CBATTErrorAttributeNotFound]; return; } - for (CBMutableCharacteristic *c in service.characteristics) { - if ([c.UUID isEqual:request.characteristic.UUID]) { - request.value = c.value; - [_condition unlock]; - [_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess]; - return; - } - } - [_condition unlock]; - [_peripheralManager respondToRequest:request withResult:CBATTErrorAttributeNotFound]; + request.value = value; + [_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess]; } #pragma mark - CBPeripheralManagerDelegate - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { - [self gnc_peripheralManagerDidUpdateState:peripheral]; + dispatch_async(_queue, ^{ + [self gnc_peripheralManagerDidUpdateState:peripheral]; + }); } - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(nullable NSError *)error { - [self gnc_peripheralManagerDidStartAdvertising:peripheral error:error]; + dispatch_async(_queue, ^{ + [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]; + dispatch_async(_queue, ^{ + [self gnc_peripheralManager:peripheral didAddService:service error:error]; + }); } - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request { - [self gnc_peripheralManager:peripheral didReceiveReadRequest:request]; + dispatch_async(_queue, ^{ + [self gnc_peripheralManager:peripheral didReceiveReadRequest:request]; + }); } @end diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h index 9ea367e8..5b0aded4 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h @@ -111,6 +111,13 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)respondToRequest:(CBATTRequest *)request withResult:(CBATTError)result; +/** + * Stops advertising peripheral manager data. + * + * Call this method when you no longer want to advertise peripheral manager data. + */ +- (void)stopAdvertising; + @end /** diff --git a/internal/platform/implementation/apple/Mediums/BUILD b/internal/platform/implementation/apple/Mediums/BUILD index d1a70ace..459b548c 100644 --- a/internal/platform/implementation/apple/Mediums/BUILD +++ b/internal/platform/implementation/apple/Mediums/BUILD @@ -18,6 +18,7 @@ package(default_visibility = ["//internal/platform/implementation/apple:__subpac objc_library( name = "Mediums", srcs = [ + "BLEv2/GNCBLEError.m", "BLEv2/GNCBLEGATTCharacteristic.m", "BLEv2/GNCBLEGATTServer.m", "BLEv2/GNCPeripheralManager.m", @@ -37,6 +38,7 @@ objc_library( "WiFiLAN/GNCWiFiLANSocket.m", ], hdrs = [ + "BLEv2/GNCBLEError.h", "BLEv2/GNCBLEGATTCharacteristic.h", "BLEv2/GNCBLEGATTServer.h", "BLEv2/GNCPeripheralManager.h", diff --git a/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m b/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m index f0531320..0a3da269 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m +++ b/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m @@ -42,17 +42,26 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - GNCBLEGATTCharacteristic *characteristic = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; + [gattServer + createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + XCTAssertNotNil(characteristic); + XCTAssertNil(error); + XCTAssertEqual(fakePeripheralManager.services.count, 1); + XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 1); + [expectation fulfill]; + }]; - XCTAssertNotNil(characteristic); - XCTAssertEqual(fakePeripheralManager.services.count, 1); - XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 1); + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testCreateMultipleCharacteristicsForOneService { @@ -63,41 +72,132 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + XCTestExpectation *expectation1 = + [[XCTestExpectation alloc] initWithDescription:@"Create characteristic 1."]; + XCTestExpectation *expectation2 = + [[XCTestExpectation alloc] initWithDescription:@"Create characteristic 2."]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID1 = [CBUUID UUIDWithString:kCharacteristicUUID1]; CBUUID *characteristicUUID2 = [CBUUID UUIDWithString:kCharacteristicUUID2]; - GNCBLEGATTCharacteristic *characteristic1 = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID1 - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; - GNCBLEGATTCharacteristic *characteristic2 = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID2 - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; + [gattServer createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID1 + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + XCTAssertNotNil(characteristic); + XCTAssertNil(error); + [expectation1 fulfill]; + }]; - XCTAssertNotNil(characteristic1); - XCTAssertNotNil(characteristic2); + [gattServer createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID2 + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + XCTAssertNotNil(characteristic); + XCTAssertNil(error); + [expectation2 fulfill]; + }]; + + [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]; + + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID1 = [CBUUID UUIDWithString:kCharacteristicUUID1]; + [gattServer createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID1 + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:nil]; + + [gattServer createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID1 + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + XCTAssertNil(characteristic); + XCTAssertNotNil(error); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; + XCTAssertEqual(fakePeripheralManager.services.count, 1); + XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 1); +} + +- (void)testCreateDuplicatePendingCharacteristics { + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID1 = [CBUUID UUIDWithString:kCharacteristicUUID1]; + [gattServer createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID1 + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:nil]; + + [gattServer createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID1 + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + XCTAssertNil(characteristic); + XCTAssertNotNil(error); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; +} + - (void)testCreateCharacteristicNotPoweredOn { GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; GNCBLEGATTServer *gattServer = [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - GNCBLEGATTCharacteristic *characteristic = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; + [gattServer createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID + permissions:CBAttributePermissionsReadable + properties:CBCharacteristicPropertyRead + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + // The error occurs after completion, so its expected to fail + // silently. + XCTAssertNotNil(characteristic); + XCTAssertNil(error); + [expectation fulfill]; + }]; - XCTAssertNil(characteristic); + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testCreateCharacteristicServiceFailure { @@ -109,101 +209,25 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 fakePeripheralManager.didAddServiceError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; - CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - GNCBLEGATTCharacteristic *characteristic = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; - - XCTAssertNil(characteristic); -} - -#pragma mark - Update Characteristic - -- (void)testUpdateCharacteristic { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create characteristic."]; CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - GNCBLEGATTCharacteristic *characteristic = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; - - BOOL success = [gattServer updateCharacteristic:characteristic value:[NSData data]]; - - XCTAssertTrue(success); - XCTAssertEqual(fakePeripheralManager.services.count, 1); - XCTAssertEqual(fakePeripheralManager.services[0].characteristics.count, 1); -} - -- (void)testUpdateCharacteristicInvalidService { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; - CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - GNCBLEGATTCharacteristic *characteristic = - [[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristicUUID serviceUUID:serviceUUID]; - - BOOL success = [gattServer updateCharacteristic:characteristic value:[NSData data]]; - - XCTAssertFalse(success); -} - -- (void)testUpdateCharacteristicInvalidCharacteristic { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; - - [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; - CBUUID *characteristicUUID1 = [CBUUID UUIDWithString:kCharacteristicUUID1]; - CBUUID *characteristicUUID2 = [CBUUID UUIDWithString:kCharacteristicUUID2]; [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID1 + characteristicUUID:characteristicUUID permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; + properties:CBCharacteristicPropertyRead + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + // The error occurs after completion, so its expected to fail + // silently. + XCTAssertNotNil(characteristic); + XCTAssertNil(error); + [expectation fulfill]; + }]; - GNCBLEGATTCharacteristic *characteristic2 = - [[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristicUUID2 serviceUUID:serviceUUID]; - - BOOL success = [gattServer updateCharacteristic:characteristic2 value:[NSData data]]; - - XCTAssertFalse(success); -} - -- (void)testUpdateCharacteristicNotPoweredOn { - GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; - - GNCBLEGATTServer *gattServer = - [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; - - CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; - CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - GNCBLEGATTCharacteristic *characteristic = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; - - BOOL success = [gattServer updateCharacteristic:characteristic value:[NSData data]]; - - XCTAssertFalse(success); + [self waitForExpectations:@[ expectation ] timeout:3]; } #pragma mark - Read Request @@ -216,15 +240,25 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create and update characteristic."]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - GNCBLEGATTCharacteristic *characteristic = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; + [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]; + }]; + }]; - [gattServer updateCharacteristic:characteristic value:[NSData data]]; + [self waitForExpectations:@[ expectation ] timeout:3]; [fakePeripheralManager simulatePeripheralManagerDidReceiveReadRequestForService:serviceUUID @@ -242,15 +276,25 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create and update characteristic."]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - GNCBLEGATTCharacteristic *characteristic = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; + [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]; + }]; + }]; - [gattServer updateCharacteristic:characteristic value:[NSData data]]; + [self waitForExpectations:@[ expectation ] timeout:3]; CBUUID *invalidServiceUUID = [CBUUID UUIDWithString:kServiceUUID2]; @@ -269,15 +313,25 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Create and update characteristic."]; + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; - GNCBLEGATTCharacteristic *characteristic = - [gattServer createCharacteristicWithServiceID:serviceUUID - characteristicUUID:characteristicUUID - permissions:CBAttributePermissionsReadable - properties:CBCharacteristicPropertyRead]; + [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]; + }]; + }]; - [gattServer updateCharacteristic:characteristic value:[NSData data]]; + [self waitForExpectations:@[ expectation ] timeout:3]; CBUUID *invalidCharacteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID2]; @@ -311,12 +365,18 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - BOOL success = [gattServer startAdvertisingData:@{}]; - XCTAssertTrue(success); - XCTAssertTrue(fakePeripheralManager.isAdvertising); - NSDictionary *data = fakePeripheralManager.advertisementData; - XCTAssertEqual(data, nil); + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer startAdvertisingData:@{} + completionHandler:^(NSError *error) { + XCTAssertNotNil(error); + XCTAssertFalse(fakePeripheralManager.isAdvertising); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testStartAdvertisingEmptyServiceData { @@ -326,16 +386,22 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - BOOL success = [gattServer startAdvertisingData:@{ - [CBUUID UUIDWithString:@"FEF3"] : [NSData data], - }]; - XCTAssertTrue(success); - XCTAssertTrue(fakePeripheralManager.isAdvertising); - NSDictionary *data = fakePeripheralManager.advertisementData; - XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @""); - XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], - [CBUUID UUIDWithString:@"FEF3"]); + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer startAdvertisingData:@{[CBUUID UUIDWithString:@"FEF3"] : [NSData data]} + completionHandler:^(NSError *error) { + XCTAssertNil(error); + XCTAssertTrue(fakePeripheralManager.isAdvertising); + NSDictionary *data = fakePeripheralManager.advertisementData; + XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @""); + XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], + [CBUUID UUIDWithString:@"FEF3"]); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testStartAdvertisingShortServiceData { @@ -345,16 +411,24 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - BOOL success = [gattServer startAdvertisingData:@{ - [CBUUID UUIDWithString:@"FEF3"] : [@"0123" dataUsingEncoding:NSUTF8StringEncoding], - }]; - XCTAssertTrue(success); - XCTAssertTrue(fakePeripheralManager.isAdvertising); - NSDictionary *data = fakePeripheralManager.advertisementData; - XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @"MDEyMw"); - XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], - [CBUUID UUIDWithString:@"FEF3"]); + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer startAdvertisingData:@{ + [CBUUID UUIDWithString:@"FEF3"] : [@"0123" dataUsingEncoding:NSUTF8StringEncoding], + } + completionHandler:^(NSError *error) { + XCTAssertNil(error); + XCTAssertTrue(fakePeripheralManager.isAdvertising); + NSDictionary *data = fakePeripheralManager.advertisementData; + XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @"MDEyMw"); + XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], + [CBUUID UUIDWithString:@"FEF3"]); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testStartAdvertising22ByteServiceData { @@ -364,16 +438,25 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - BOOL success = [gattServer startAdvertisingData:@{ - [CBUUID UUIDWithString:@"FEF3"] : [@"0123456789012345" dataUsingEncoding:NSUTF8StringEncoding], - }]; - XCTAssertTrue(success); - XCTAssertTrue(fakePeripheralManager.isAdvertising); - NSDictionary *data = fakePeripheralManager.advertisementData; - XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @"MDEyMzQ1Njc4OTAxMjM0NQ"); - XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], - [CBUUID UUIDWithString:@"FEF3"]); + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer startAdvertisingData:@{ + [CBUUID UUIDWithString:@"FEF3"] : [@"0123456789012345" dataUsingEncoding:NSUTF8StringEncoding], + } + completionHandler:^(NSError *error) { + XCTAssertNil(error); + XCTAssertTrue(fakePeripheralManager.isAdvertising); + NSDictionary *data = fakePeripheralManager.advertisementData; + XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], + @"MDEyMzQ1Njc4OTAxMjM0NQ"); + XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], + [CBUUID UUIDWithString:@"FEF3"]); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testStartAdvertisingLongServiceData { @@ -383,17 +466,26 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - BOOL success = [gattServer startAdvertisingData:@{ - [CBUUID UUIDWithString:@"FEF3"] : - [@"012345678901234567890123456789" dataUsingEncoding:NSUTF8StringEncoding], - }]; - XCTAssertTrue(success); - XCTAssertTrue(fakePeripheralManager.isAdvertising); - NSDictionary *data = fakePeripheralManager.advertisementData; - XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @"MDEyMzQ1Njc4OTAxMjM0NT"); - XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], - [CBUUID UUIDWithString:@"FEF3"]); + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer + startAdvertisingData:@{ + [CBUUID UUIDWithString:@"FEF3"] : + [@"012345678901234567890123456789" dataUsingEncoding:NSUTF8StringEncoding], + } + completionHandler:^(NSError *error) { + XCTAssertNil(error); + XCTAssertTrue(fakePeripheralManager.isAdvertising); + NSDictionary *data = fakePeripheralManager.advertisementData; + XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @"MDEyMzQ1Njc4OTAxMjM0NT"); + XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], + [CBUUID UUIDWithString:@"FEF3"]); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testStartAdvertisingWithEmojiServiceData { @@ -403,16 +495,25 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - BOOL success = [gattServer startAdvertisingData:@{ - [CBUUID UUIDWithString:@"FEF3"] : [@"😁❤️🤡" dataUsingEncoding:NSUTF8StringEncoding], - }]; - XCTAssertTrue(success); - XCTAssertTrue(fakePeripheralManager.isAdvertising); - NSDictionary *data = fakePeripheralManager.advertisementData; - XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @"8J-YgeKdpO-4j_CfpKE"); - XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], - [CBUUID UUIDWithString:@"FEF3"]); + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer + startAdvertisingData:@{ + [CBUUID UUIDWithString:@"FEF3"] : [@"😁❤️🤡" dataUsingEncoding:NSUTF8StringEncoding], + } + completionHandler:^(NSError *error) { + XCTAssertNil(error); + XCTAssertTrue(fakePeripheralManager.isAdvertising); + NSDictionary *data = fakePeripheralManager.advertisementData; + XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @"8J-YgeKdpO-4j_CfpKE"); + XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], + [CBUUID UUIDWithString:@"FEF3"]); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testStartAdvertisingMultipleServices { @@ -422,15 +523,23 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - BOOL success = [gattServer startAdvertisingData:@{ + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer startAdvertisingData:@{ [CBUUID UUIDWithString:@"FEF3"] : [@"012345678901234567890123456789" dataUsingEncoding:NSUTF8StringEncoding], [CBUUID UUIDWithString:@"FEF4"] : [@"012345678901234567890123456789" dataUsingEncoding:NSUTF8StringEncoding], - }]; + } + completionHandler:^(NSError *error) { + XCTAssertNotNil(error); + XCTAssertFalse(fakePeripheralManager.isAdvertising); + [expectation fulfill]; + }]; - XCTAssertFalse(success); - XCTAssertFalse(fakePeripheralManager.isAdvertising); + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testStartAdvertisingNotPoweredOn { @@ -439,10 +548,18 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 GNCBLEGATTServer *gattServer = [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; - BOOL success = [gattServer startAdvertisingData:@{}]; + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; - XCTAssertFalse(success); - XCTAssertFalse(fakePeripheralManager.isAdvertising); + [gattServer startAdvertisingData:@{[CBUUID UUIDWithString:@"FEF3"] : [NSData data]} + completionHandler:^(NSError *error) { + // The error occurs after completion, so its expected to fail silently. + XCTAssertNil(error); + XCTAssertFalse(fakePeripheralManager.isAdvertising); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; } - (void)testStartAdvertisingStartFailure { @@ -455,10 +572,46 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 code:0 userInfo:nil]; [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; - BOOL success = [gattServer startAdvertisingData:@{}]; - XCTAssertFalse(success); - XCTAssertFalse(fakePeripheralManager.isAdvertising); + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer startAdvertisingData:@{[CBUUID UUIDWithString:@"FEF3"] : [NSData data]} + completionHandler:^(NSError *error) { + // The error occurs after completion, so its expected to fail silently. + XCTAssertNil(error); + XCTAssertFalse(fakePeripheralManager.isAdvertising); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; +} + +- (void)testStartAdvertisingAlreadyAdvertising { + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + + fakePeripheralManager.didStartAdvertisingError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer startAdvertisingData:@{[CBUUID UUIDWithString:@"FEF3"] : [NSData data]} + completionHandler:nil]; + + [gattServer startAdvertisingData:@{[CBUUID UUIDWithString:@"FEF4"] : [NSData data]} + completionHandler:^(NSError *error) { + XCTAssertNotNil(error); + XCTAssertFalse(fakePeripheralManager.isAdvertising); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; } @end diff --git a/internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.m b/internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.m index 96cbd8ea..184e5688 100644 --- a/internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.m +++ b/internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.m @@ -61,9 +61,6 @@ BOOL _isAdvertising; NSDictionary *_advertisementData; NSMutableArray *_services; - - // Used to deliver delegate callbacks. - dispatch_queue_t _queue; } @synthesize peripheralDelegate; @@ -79,7 +76,6 @@ _state = CBManagerStateUnknown; _advertisementData = nil; _services = [[NSMutableArray alloc] init]; - _queue = dispatch_queue_create("com.nearby.GNCFakePeripheralManager", DISPATCH_QUEUE_SERIAL); } return self; } @@ -96,9 +92,7 @@ if (!_didAddServiceError) { [_services addObject:service]; } - dispatch_async(_queue, ^{ - [peripheralDelegate gnc_peripheralManager:self didAddService:service error:_didAddServiceError]; - }); + [peripheralDelegate gnc_peripheralManager:self didAddService:service error:_didAddServiceError]; } - (void)removeService:(CBMutableService *)service { @@ -112,10 +106,8 @@ - (void)startAdvertising:(NSDictionary *)advertisementData { _isAdvertising = _didStartAdvertisingError == nil; _advertisementData = advertisementData; - dispatch_async(_queue, ^{ - [peripheralDelegate gnc_peripheralManagerDidStartAdvertising:self - error:_didStartAdvertisingError]; - }); + [peripheralDelegate gnc_peripheralManagerDidStartAdvertising:self + error:_didStartAdvertisingError]; } - (void)respondToRequest:(CBATTRequest *)request withResult:(CBATTError)result { @@ -126,6 +118,9 @@ [_respondToRequestErrorExpectation fulfill]; } +- (void)stopAdvertising { +} + #pragma mark - Testing Helpers - (NSArray *)services { @@ -138,18 +133,14 @@ - (void)simulatePeripheralManagerDidUpdateState:(CBManagerState)fakeState { _state = fakeState; - dispatch_async(_queue, ^{ - [peripheralDelegate gnc_peripheralManagerDidUpdateState:self]; - }); + [peripheralDelegate gnc_peripheralManagerDidUpdateState:self]; } - (void)simulatePeripheralManagerDidReceiveReadRequestForService:(CBUUID *)service characteristic:(CBUUID *)characteristic { CBATTRequest *request = [[CBATTRequest alloc] initWithService:service characteristic:characteristic]; - dispatch_async(_queue, ^{ - [peripheralDelegate gnc_peripheralManager:self didReceiveReadRequest:request]; - }); + [peripheralDelegate gnc_peripheralManager:self didReceiveReadRequest:request]; } @end From eb5f8aed10a9b44aeb4196fc7b0445c2e81a082d Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Thu, 27 Jul 2023 18:56:26 -0700 Subject: [PATCH 029/128] Fixed WI-FI hotspot stuck issue during close PiperOrigin-RevId: 551703821 --- .../implementation/windows/wifi_hotspot.h | 18 +++- .../windows/wifi_hotspot_server_socket.cc | 84 ++++++++++++------- 2 files changed, 72 insertions(+), 30 deletions(-) diff --git a/internal/platform/implementation/windows/wifi_hotspot.h b/internal/platform/implementation/windows/wifi_hotspot.h index ba019179..5c34e414 100644 --- a/internal/platform/implementation/windows/wifi_hotspot.h +++ b/internal/platform/implementation/windows/wifi_hotspot.h @@ -16,8 +16,8 @@ #define PLATFORM_IMPL_WINDOWS_WIFI_HOTSPOT_H_ // Windows headers -#include #include +#include #include // Standard C/C++ headers @@ -27,6 +27,7 @@ #include // Nearby connections headers +#include "absl/strings/string_view.h" #include "internal/platform/implementation/wifi_hotspot.h" #include "internal/platform/implementation/windows/scheduled_executor.h" #include "internal/platform/implementation/windows/submittable_executor.h" @@ -123,7 +124,7 @@ class WifiHotspotSocket : public api::WifiHotspotSocket { Exception Close() override; private: - enum class SocketType {kWinRTSocket = 0, kWin32Socket}; + enum class SocketType { kWinRTSocket = 0, kWin32Socket }; // A simple wrapper to handle input stream of socket class SocketInputStream : public InputStream { public: @@ -204,6 +205,10 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket { bool listen(); private: + static constexpr int kSocketEventsCount = 2; + static constexpr int kSocketEventListen = 0; + static constexpr int kSocketEventClose = 1; + // The listener is accepting incoming connections fire_and_forget Listener_ConnectionReceived( StreamSocketListener listener, @@ -214,6 +219,8 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket { // Retrieves hotspot IP address from local machine std::string GetHotspotIpAddress() const; + void SocketErrorNotice(absl::string_view reason); + mutable absl::Mutex mutex_; absl::CondVar cond_; SubmittableExecutor submittable_executor_; @@ -225,6 +232,13 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket { std::deque pending_client_sockets_ ABSL_GUARDED_BY(mutex_); SOCKET listen_socket_ = INVALID_SOCKET; SOCKET client_socket_ = INVALID_SOCKET; + + // closesocket cannot trigger FD_CLOSE on listener socket. In order to avoid + // blocking in WSAWaitForMultipleEvents, we use a socket event to trigger + // WSAWaitForMultipleEvents safely. + // The socket_events_ has 2 events, the first one is to handle normal socket + // event, and the second one is to handle event to close the socket manually. + WSAEVENT socket_events_[kSocketEventsCount]; // Close notifier absl::AnyInvocable close_notifier_ = nullptr; diff --git a/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc index 35429cbb..a9d2f6f2 100644 --- a/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc +++ b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc @@ -36,7 +36,11 @@ namespace { using ::winrt::Windows::Networking::Sockets::SocketQualityOfService; } // namespace -WifiHotspotServerSocket::WifiHotspotServerSocket(int port) : port_(port) {} +WifiHotspotServerSocket::WifiHotspotServerSocket(int port) : port_(port) { + for (auto &it : socket_events_) { + it = WSA_INVALID_EVENT; + } +} WifiHotspotServerSocket::~WifiHotspotServerSocket() { Close(); } @@ -55,7 +59,7 @@ std::string WifiHotspotServerSocket::GetIPAddress() const { std::string hotspot_ip_address = GetHotspotIpAddress(); NEARBY_LOGS(INFO) << __func__ - << ": Return hotspot IP address: " << hotspot_ip_address; + << ": Return hotspot IP address: " << hotspot_ip_address; return hotspot_ip_address; } @@ -126,18 +130,26 @@ Exception WifiHotspotServerSocket::Close() { kEnableHotspotWin32Socket)) { if (listen_socket_ != INVALID_SOCKET) { NEARBY_LOGS(INFO) << ": Close listen_socket_: " << listen_socket_; + // Trigger close event manually + WSASetEvent(socket_events_[kSocketEventClose]); + shutdown(listen_socket_, 2); + shutdown(client_socket_, 2); closesocket(listen_socket_); closesocket(client_socket_); - listen_socket_ = INVALID_SOCKET; - client_socket_ = INVALID_SOCKET; for (const auto &pending_socket : pending_client_sockets_) { if (pending_socket != INVALID_SOCKET) closesocket(pending_socket); } + submittable_executor_.Shutdown(); + listen_socket_ = INVALID_SOCKET; + client_socket_ = INVALID_SOCKET; + for (auto &it : socket_events_) { + WSACloseEvent(it); + it = WSA_INVALID_EVENT; + } WSACleanup(); pending_client_sockets_ = {}; } - submittable_executor_.Shutdown(); } else { if (stream_socket_listener_ != nullptr) { stream_socket_listener_.ConnectionReceived(listener_event_token_); @@ -254,14 +266,16 @@ bool WifiHotspotServerSocket::SetupServerSocketWinRT() { return false; } -// Checks for SOCKET_ERROR, this error can come up when trying to bind, listen, -// Getsockname, WSACreateEvent, WSAEventSelect etc. -void SocketErrorNotice(SOCKET socket_to_close, const char *action) { - // const char *actionAttempted = action; - NEARBY_LOGS(WARNING) << "socket error. " << action +void WifiHotspotServerSocket::SocketErrorNotice(absl::string_view reason) { + NEARBY_LOGS(WARNING) << "socket error. " << reason << " failed with error: " << WSAGetLastError(); - - closesocket(socket_to_close); + for (auto &it : socket_events_) { + if (it != WSA_INVALID_EVENT) { + WSACloseEvent(it); + it = WSA_INVALID_EVENT; + } + } + closesocket(listen_socket_); WSACleanup(); } @@ -293,7 +307,7 @@ bool WifiHotspotServerSocket::SetupServerSocketWinSock() { sizeof(flag)); if (bind(listen_socket_, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) { - SocketErrorNotice(listen_socket_, "Bind"); + SocketErrorNotice("Bind"); return false; } NEARBY_LOGS(INFO) << "Bind socket successful"; @@ -302,50 +316,63 @@ bool WifiHotspotServerSocket::SetupServerSocketWinSock() { memset(&serv_addr, 0, size); if (getsockname(listen_socket_, (struct sockaddr *)&serv_addr, &size) == SOCKET_ERROR) { - SocketErrorNotice(listen_socket_, "Getsockname"); + SocketErrorNotice("Getsockname"); return false; } port_ = ntohs(serv_addr.sin_port); NEARBY_LOGS(INFO) << "Hotspot Server bound to port: " << port_; - socket_event = WSACreateEvent(); - if (socket_event == nullptr) { - SocketErrorNotice(listen_socket_, "WSACreateEvent"); + socket_events_[kSocketEventListen] = WSACreateEvent(); + if (socket_events_[kSocketEventListen] == WSA_INVALID_EVENT) { + SocketErrorNotice("WSACreateEvent"); return false; } + + socket_events_[kSocketEventClose] = WSACreateEvent(); + if (socket_events_[kSocketEventClose] == WSA_INVALID_EVENT) { + SocketErrorNotice("WSACreateEvent"); + return false; + } + // Associate event types FD_ACCEPT and FD_CLOSE with the listen_socket_ and // socket_event - if (WSAEventSelect(listen_socket_, socket_event, FD_ACCEPT | FD_CLOSE) == - SOCKET_ERROR) { - SocketErrorNotice(listen_socket_, "WSAEventSelect"); + if (WSAEventSelect(listen_socket_, socket_events_[kSocketEventListen], + FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR) { + SocketErrorNotice("WSAEventSelect"); return false; } if (::listen(listen_socket_, SOMAXCONN) == SOCKET_ERROR) { - SocketErrorNotice(listen_socket_, "Listen"); + SocketErrorNotice("Listen"); return false; } NEARBY_LOGS(INFO) << "Hotspot Server Socket " << listen_socket_ << " started to listen with socket event: " << socket_event; - submittable_executor_.Execute([this, socket_event]() { + submittable_executor_.Execute([this]() { DWORD index; WSANETWORKEVENTS network_events; // Wait for network events on all sockets - index = - WSAWaitForMultipleEvents(1, &socket_event, FALSE, WSA_INFINITE, FALSE); + index = WSAWaitForMultipleEvents(kSocketEventsCount, socket_events_, FALSE, + WSA_INFINITE, FALSE); NEARBY_LOGS(INFO) << "Hotspot Server Socket " << listen_socket_ - << " received event: " << socket_event; + << " received event index: " << index; if (index == WSA_WAIT_TIMEOUT || index == WSA_WAIT_FAILED) { NEARBY_LOGS(INFO) << "Hotspot Server Socket timout or failed "; return false; } index = index - WSA_WAIT_EVENT_0; + if (index == kSocketEventClose) { + // the socket is closed by SDK + NEARBY_LOGS(INFO) << "listner socket is closed."; + return false; + } + // Iterate through all events and enumerate - if (WSAEnumNetworkEvents(listen_socket_, socket_event, &network_events) == - SOCKET_ERROR) { + if (WSAEnumNetworkEvents(listen_socket_, socket_events_[index], + &network_events) == SOCKET_ERROR) { NEARBY_LOGS(INFO) << "Iterate through all events failed"; return false; } @@ -361,7 +388,8 @@ bool WifiHotspotServerSocket::SetupServerSocketWinSock() { return false; } - if (WSAEventSelect(listen_socket_, socket_event, 0) == SOCKET_ERROR) { + if (WSAEventSelect(listen_socket_, socket_events_[kSocketEventListen], + 0) == SOCKET_ERROR) { NEARBY_LOGS(WARNING) << "Remove association between listen_socket_ and event failed: " << WSAGetLastError(); From 6d886e3fc84d7fbd4974dd1e7e9de9c774af0922 Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Fri, 28 Jul 2023 11:12:13 -0700 Subject: [PATCH 030/128] Fixed some issues in analytics data PiperOrigin-RevId: 551900985 --- .../analytics/analytics_recorder.cc | 28 +++++++++++++++---- .../analytics/analytics_recorder.h | 8 ++++-- .../implementation/base_pcp_handler.cc | 3 ++ connections/implementation/bwu_manager.cc | 7 +++-- connections/implementation/client_proxy.cc | 10 +++++-- connections/implementation/client_proxy.h | 14 +++++++--- connections/implementation/payload_manager.cc | 4 +++ 7 files changed, 58 insertions(+), 16 deletions(-) diff --git a/connections/implementation/analytics/analytics_recorder.cc b/connections/implementation/analytics/analytics_recorder.cc index 60b90a90..cb50febf 100644 --- a/connections/implementation/analytics/analytics_recorder.cc +++ b/connections/implementation/analytics/analytics_recorder.cc @@ -220,7 +220,7 @@ void AnalyticsRecorder::OnStartedIncomingConnectionListening( return; } UpdateStrategySessionLocked(strategy, ADVERTISER); - if (started_advertising_phase_time_ == absl::Now()) { + if (started_advertising_phase_time_ == absl::InfinitePast()) { started_advertising_phase_time_ = SystemClock::ElapsedRealtime(); } } @@ -250,6 +250,19 @@ void AnalyticsRecorder::OnEndpointFound(Medium medium) { 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_); @@ -1034,10 +1047,15 @@ void AnalyticsRecorder::FinishStrategySessionLocked() { bandwidth_upgrade_attempts_.clear(); // Add the StrategySession in ClientSession - current_strategy_session_->set_duration_millis(absl::ToInt64Milliseconds( - started_strategy_session_time_ - SystemClock::ElapsedRealtime())); - *client_session_->add_strategy_session() = - *std::move(current_strategy_session_); + 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); } } diff --git a/connections/implementation/analytics/analytics_recorder.h b/connections/implementation/analytics/analytics_recorder.h index d082e3e8..141022af 100644 --- a/connections/implementation/analytics/analytics_recorder.h +++ b/connections/implementation/analytics/analytics_recorder.h @@ -74,6 +74,10 @@ class AnalyticsRecorder { ABSL_LOCKS_EXCLUDED(mutex_); // Connection request + void OnRequestConnection(const connections::Strategy &strategy, + const std::string &endpoint_id) + ABSL_LOCKS_EXCLUDED(mutex_); + void OnConnectionRequestReceived(const std::string &remote_endpoint_id) ABSL_LOCKS_EXCLUDED(mutex_); void OnConnectionRequestSent(const std::string &remote_endpoint_id) @@ -374,13 +378,13 @@ class AnalyticsRecorder { std::unique_ptr< location::nearby::analytics::proto::ConnectionsLog::AdvertisingPhase> current_advertising_phase_; - absl::Time started_advertising_phase_time_; + 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::Time started_discovery_phase_time_ = absl::InfinitePast(); absl::btree_map GetConnectionInfoFromResult( @@ -379,9 +382,8 @@ class BasePcpHandler : public PcpHandler, } // Test only. - absl::flat_hash_map>& - GetEndpointLostByMediumAlarms() { - return endpoint_lost_by_medium_alarms_; + int GetEndpointLostByMediumAlarmsCount() RUN_ON_PCP_HANDLER_THREAD() { + return endpoint_lost_by_medium_alarms_.size(); } Mediums* mediums_; @@ -603,7 +605,7 @@ class BasePcpHandler : public PcpHandler, // Mapping from endpoint_id -> CancelableAlarm for triggering endpoint loss // while discovery options are updated. absl::flat_hash_map> - endpoint_lost_by_medium_alarms_; + endpoint_lost_by_medium_alarms_ ABSL_GUARDED_BY(GetPcpHandlerThread()); Pcp pcp_; Strategy strategy_{PcpToStrategy(pcp_)}; diff --git a/connections/implementation/base_pcp_handler_test.cc b/connections/implementation/base_pcp_handler_test.cc index 28343a0d..9e849bef 100644 --- a/connections/implementation/base_pcp_handler_test.cc +++ b/connections/implementation/base_pcp_handler_test.cc @@ -233,19 +233,36 @@ class MockPcpHandler : public BasePcpHandler { return BasePcpHandler::GetDiscoveredEndpoints(medium); } - absl::flat_hash_map>& - GetEndpointLostByMediumAlarms() { - return BasePcpHandler::GetEndpointLostByMediumAlarms(); + int GetEndpointLostByMediumAlarmsCount() { + Future alarms_count; + RunOnPcpHandlerThread( + "GetEndpointLostByMediumAlarmsCount", + [this, alarms_count]() RUN_ON_PCP_HANDLER_THREAD() mutable { + alarms_count.Set( + BasePcpHandler::GetEndpointLostByMediumAlarmsCount()); + }); + return alarms_count.Get().result(); } void StartEndpointLostByMediumAlarms( - ClientProxy* client, location::nearby::proto::connections::Medium medium) { - BasePcpHandler::StartEndpointLostByMediumAlarms(client, medium); + ClientProxy* client, + location::nearby::proto::connections::Medium medium) { + RunOnPcpHandlerThread("StartEndpointLostByMediumAlarms", + [this, client, medium]() RUN_ON_PCP_HANDLER_THREAD() { + BasePcpHandler::StartEndpointLostByMediumAlarms( + client, medium); + }); } - void StopEndpointLostByMediumAlarm(absl::string_view endpoint_id, - location::nearby::proto::connections::Medium medium) { - BasePcpHandler::StopEndpointLostByMediumAlarm(endpoint_id, medium); + void StopEndpointLostByMediumAlarm( + absl::string_view endpoint_id, + location::nearby::proto::connections::Medium medium) { + RunOnPcpHandlerThread("StopEndpointLostByMediumAlarm", + [this, endpoint_id = std::string(endpoint_id), + medium]() RUN_ON_PCP_HANDLER_THREAD() { + BasePcpHandler::StopEndpointLostByMediumAlarm( + endpoint_id, medium); + }); } std::vector GetDiscoveryMediums( @@ -553,8 +570,7 @@ class BasePcpHandlerTest const std::string& endpoint_id, std::unique_ptr channel_a, MockEndpointChannel* channel_b, ClientProxy* client, - MockPcpHandler* pcp_handler, - std::atomic_int* flag = nullptr, + MockPcpHandler* pcp_handler, std::atomic_int* flag = nullptr, Status expected_result = {Status::kSuccess}) { ConnectionRequestInfo info{ .endpoint_info = ByteArray{"ABCD"}, @@ -581,29 +597,29 @@ class BasePcpHandlerTest auto allowed_mediums = pcp_handler->GetDiscoveryMediums(client); EXPECT_CALL(*pcp_handler, ConnectImpl) - .WillRepeatedly(Invoke([&channel_a]( - ClientProxy* client, - MockPcpHandler::DiscoveredEndpoint* endpoint) { - if (endpoint->medium == - location::nearby::proto::connections::WIFI_LAN) { - NEARBY_LOGS(INFO) << "Connect with Medium WIFI_LAN failed."; - return MockPcpHandler::ConnectImplResult{ - .medium = endpoint->medium, - .status = {Status::kError}, - .endpoint_channel = nullptr, - }; - } else { - NEARBY_LOGS(INFO) - << "Connect with Medium: " - << location::nearby::proto::connections::Medium_Name( - endpoint->medium); - return MockPcpHandler::ConnectImplResult{ - .medium = endpoint->medium, - .status = {Status::kSuccess}, - .endpoint_channel = std::move(channel_a), - }; - } - })); + .WillRepeatedly( + Invoke([&channel_a](ClientProxy* client, + MockPcpHandler::DiscoveredEndpoint* endpoint) { + if (endpoint->medium == + location::nearby::proto::connections::WIFI_LAN) { + NEARBY_LOGS(INFO) << "Connect with Medium WIFI_LAN failed."; + return MockPcpHandler::ConnectImplResult{ + .medium = endpoint->medium, + .status = {Status::kError}, + .endpoint_channel = nullptr, + }; + } else { + NEARBY_LOGS(INFO) + << "Connect with Medium: " + << location::nearby::proto::connections::Medium_Name( + endpoint->medium); + return MockPcpHandler::ConnectImplResult{ + .medium = endpoint->medium, + .status = {Status::kSuccess}, + .endpoint_channel = std::move(channel_a), + }; + } + })); for (const auto& discovered_medium : allowed_mediums) { pcp_handler->OnEndpointFound( @@ -1247,9 +1263,9 @@ TEST_F(BasePcpHandlerTest, TestStartStopEndpointLostAlarm) { .status = {Status::kSuccess}, .mediums = allowed.GetMediums(true), })); - EXPECT_EQ(pcp_handler.StartDiscovery(&client, service_id, discovery_options, - {}), - Status{Status::kSuccess}); + EXPECT_EQ( + pcp_handler.StartDiscovery(&client, service_id, discovery_options, {}), + Status{Status::kSuccess}); EXPECT_TRUE(client.IsDiscovering()); EXPECT_CALL(pcp_handler, InjectEndpointImpl) @@ -1277,11 +1293,11 @@ TEST_F(BasePcpHandlerTest, TestStartStopEndpointLostAlarm) { .remote_bluetooth_mac_address = ByteArray(kFakeMacAddress), }); EXPECT_EQ(pcp_handler.GetDiscoveredEndpoints(Medium::BLUETOOTH).size(), 1); - EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarmsCount(), 0); pcp_handler.StartEndpointLostByMediumAlarms(&client, Medium::BLUETOOTH); - EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 1); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarmsCount(), 1); pcp_handler.StopEndpointLostByMediumAlarm(endpoint_id, Medium::BLUETOOTH); - EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarmsCount(), 0); env_.Stop(); } @@ -1311,9 +1327,9 @@ TEST_F(BasePcpHandlerTest, TestStartEndpointLostByMediumAlarms) { .status = {Status::kSuccess}, .mediums = allowed.GetMediums(true), })); - EXPECT_EQ(pcp_handler.StartDiscovery(&client, service_id, discovery_options, - {}), - Status{Status::kSuccess}); + EXPECT_EQ( + pcp_handler.StartDiscovery(&client, service_id, discovery_options, {}), + Status{Status::kSuccess}); EXPECT_TRUE(client.IsDiscovering()); EXPECT_CALL(pcp_handler, InjectEndpointImpl) @@ -1341,12 +1357,12 @@ TEST_F(BasePcpHandlerTest, TestStartEndpointLostByMediumAlarms) { .remote_bluetooth_mac_address = ByteArray(kFakeMacAddress), }); EXPECT_EQ(pcp_handler.GetDiscoveredEndpoints(Medium::BLUETOOTH).size(), 1); - EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarmsCount(), 0); pcp_handler.StartEndpointLostByMediumAlarms(&client, Medium::BLUETOOTH); - EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 1); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarmsCount(), 1); absl::SleepFor(absl::Seconds(11)); EXPECT_EQ(pcp_handler.GetDiscoveredEndpoints(Medium::BLUETOOTH).size(), 0); - EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarmsCount(), 0); env_.Stop(); } @@ -1376,37 +1392,39 @@ TEST_F(BasePcpHandlerTest, TestEndpointFoundStopsAlarm) { .status = {Status::kSuccess}, .mediums = allowed.GetMediums(true), })); - EXPECT_EQ(pcp_handler.StartDiscovery(&client, service_id, discovery_options, - {}), - Status{Status::kSuccess}); + EXPECT_EQ( + pcp_handler.StartDiscovery(&client, service_id, discovery_options, {}), + Status{Status::kSuccess}); EXPECT_TRUE(client.IsDiscovering()); bool first_call = true; - EXPECT_CALL(pcp_handler, InjectEndpointImpl).Times(2) - .WillRepeatedly(Invoke([&pcp_handler, &endpoint_id, &first_call]( - ClientProxy* client, const std::string& service_id, - const OutOfBandConnectionMetadata& metadata) { - ByteArray endpoint_info; - if (first_call) { - endpoint_info = ByteArray("ABCD"); - } else { - endpoint_info = ByteArray("ABCDE"); - } - first_call = false; - pcp_handler.OnEndpointFound( - client, - std::make_shared(MockDiscoveredEndpoint{ - { - endpoint_id, - endpoint_info, - service_id, - Medium::BLUETOOTH, - WebRtcState::kUndefined, - }, - MockContext{nullptr}, - })); - return Status{Status::kSuccess}; - })); + EXPECT_CALL(pcp_handler, InjectEndpointImpl) + .Times(2) + .WillRepeatedly( + Invoke([&pcp_handler, &endpoint_id, &first_call]( + ClientProxy* client, const std::string& service_id, + const OutOfBandConnectionMetadata& metadata) { + ByteArray endpoint_info; + if (first_call) { + endpoint_info = ByteArray("ABCD"); + } else { + endpoint_info = ByteArray("ABCDE"); + } + first_call = false; + pcp_handler.OnEndpointFound( + client, + std::make_shared(MockDiscoveredEndpoint{ + { + endpoint_id, + endpoint_info, + service_id, + Medium::BLUETOOTH, + WebRtcState::kUndefined, + }, + MockContext{nullptr}, + })); + return Status{Status::kSuccess}; + })); pcp_handler.InjectEndpoint( &client, service_id, OutOfBandConnectionMetadata{ @@ -1414,16 +1432,16 @@ TEST_F(BasePcpHandlerTest, TestEndpointFoundStopsAlarm) { .remote_bluetooth_mac_address = ByteArray(kFakeMacAddress), }); EXPECT_EQ(pcp_handler.GetDiscoveredEndpoints(Medium::BLUETOOTH).size(), 1); - EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarmsCount(), 0); pcp_handler.StartEndpointLostByMediumAlarms(&client, Medium::BLUETOOTH); - EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 1); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarmsCount(), 1); pcp_handler.InjectEndpoint( &client, service_id, OutOfBandConnectionMetadata{ .medium = Medium::BLUETOOTH, .remote_bluetooth_mac_address = ByteArray(kFakeMacAddress), }); - EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarms().size(), 0); + EXPECT_EQ(pcp_handler.GetEndpointLostByMediumAlarmsCount(), 0); env_.Stop(); } diff --git a/connections/implementation/p2p_cluster_pcp_handler.h b/connections/implementation/p2p_cluster_pcp_handler.h index a53fc8fd..aa1c3950 100644 --- a/connections/implementation/p2p_cluster_pcp_handler.h +++ b/connections/implementation/p2p_cluster_pcp_handler.h @@ -113,7 +113,8 @@ class P2pClusterPcpHandler : public BasePcpHandler { ClientProxy* client, absl::string_view service_id, absl::string_view local_endpoint_id, absl::string_view local_endpoint_info, - const DiscoveryOptions& discovery_options) override; + const DiscoveryOptions& discovery_options) + RUN_ON_PCP_HANDLER_THREAD() override; private: // Holds the state required to re-create a BleEndpoint we see on a From c1d74120a0ebafa9fd74d4f36ac0f340e61c5300 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Fri, 28 Jul 2023 12:31:30 -0700 Subject: [PATCH 032/128] Add initial BLEv2 Bluetooth Adapter PiperOrigin-RevId: 551922301 --- internal/platform/implementation/BUILD | 1 - internal/platform/implementation/apple/BUILD | 2 + .../apple/bluetooth_adapter_v2.h | 90 +++++++++++++++++++ .../apple/bluetooth_adapter_v2.mm | 71 +++++++++++++++ 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 internal/platform/implementation/apple/bluetooth_adapter_v2.h create mode 100644 internal/platform/implementation/apple/bluetooth_adapter_v2.mm diff --git a/internal/platform/implementation/BUILD b/internal/platform/implementation/BUILD index a9203745..1eeb0b58 100644 --- a/internal/platform/implementation/BUILD +++ b/internal/platform/implementation/BUILD @@ -101,7 +101,6 @@ cc_library( # TODO: Support WebRTC "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", - "@com_google_absl//absl/log:check", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index af77fa20..98af2cba 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -111,6 +111,7 @@ objc_library( "ble_server_socket.mm", "ble_socket.mm", "ble_utils.mm", + "bluetooth_adapter_v2.mm", "utils.mm", ], hdrs = [ @@ -118,6 +119,7 @@ objc_library( "ble_server_socket.h", "ble_socket.h", "ble_utils.h", + "bluetooth_adapter_v2.h", "utils.h", ], # Prevent Objective-C++ headers from being pulled into swift. diff --git a/internal/platform/implementation/apple/bluetooth_adapter_v2.h b/internal/platform/implementation/apple/bluetooth_adapter_v2.h new file mode 100644 index 00000000..f1c09b3e --- /dev/null +++ b/internal/platform/implementation/apple/bluetooth_adapter_v2.h @@ -0,0 +1,90 @@ +// 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. + +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data + +#import + +#include + +#include "absl/strings/string_view.h" +#include "internal/platform/implementation/bluetooth_adapter.h" + +namespace nearby { +namespace apple { + +// Concrete implementation representing the local device Bluetooth adapter. +class BluetoothAdapter : public api::BluetoothAdapter { + public: + BluetoothAdapter() = default; + ~BluetoothAdapter() override = default; + + // Synchronously sets the status of the BluetoothAdapter to @c status, and + // returns true if the operation was a success. + bool SetStatus(api::BluetoothAdapter::Status status) override; + + // Returns true if the BluetoothAdapter's current status is @c kEnabled. + bool IsEnabled() const override; + + // Get the current Bluetooth scan mode of the local Bluetooth adapter. + // + // The Bluetooth scan mode determines if the local adapter is connectable + // and/or discoverable from remote Bluetooth devices. + // + // Possible values are: @c kNone, @c kConnectable, + // @c kConnectableDiscoverable. + // + // Returns @c kUnknown on error. + api::BluetoothAdapter::ScanMode GetScanMode() const override; + + // Synchronously sets the scan mode of the adapter, and returns true if the + // operation was a success. + bool SetScanMode(api::BluetoothAdapter::ScanMode scan_mode) override; + + // Get the friendly Bluetooth name of the local Bluetooth adapter. + // + // Returns an empty string on error + std::string GetName() const override; + + // Set and persist the friendly Bluetooth name of the local Bluetooth adapter. + // + // This name is visible to remote Bluetooth devices. + // + // Valid Bluetooth names are a maximum of 248 bytes using UTF-8 encoding, + // although many remote devices can only display the first 40 characters, and + // some may be limited to just 20. + bool SetName(absl::string_view name) override; + + // Set the friendly Bluetooth name of the local Bluetooth adapter. + // + // This name is visible to remote Bluetooth devices. + // + // Valid Bluetooth names are a maximum of 248 bytes using UTF-8 encoding, + // although many remote devices can only display the first 40 characters, and + // some may be limited to just 20. + // + // If persist is false, we will restore the original radio names when complete. + bool SetName(absl::string_view name, bool persist) override; + + // Returns BT MAC address assigned to this adapter. + std::string GetMacAddress() const override; +}; + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/bluetooth_adapter_v2.mm b/internal/platform/implementation/apple/bluetooth_adapter_v2.mm new file mode 100644 index 00000000..a6c6bfe7 --- /dev/null +++ b/internal/platform/implementation/apple/bluetooth_adapter_v2.mm @@ -0,0 +1,71 @@ +// 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. + +#import "internal/platform/implementation/apple/bluetooth_adapter_v2.h" + +#import + +#include + +#include "absl/strings/string_view.h" +#include "internal/platform/implementation/bluetooth_adapter.h" + +namespace nearby { +namespace apple { + +bool BluetoothAdapter::SetStatus(api::BluetoothAdapter::Status status) { + // We can't force a radio state in macOS/iOS. + // NOTE: This is supposed to return the success/failure of the change (which should always be + // "fail" for our case). However, usage in the codebase expects this to return with "success" if + // the radio is on, so we just return whether the radio is currently enabled. + return IsEnabled(); +} + +// TODO(b/290385712): Implement. +bool BluetoothAdapter::IsEnabled() const { + return true; +} + +// TODO(b/290385712): Implement. +api::BluetoothAdapter::ScanMode BluetoothAdapter::GetScanMode() const { + return api::BluetoothAdapter::ScanMode::kUnknown; +} + +// TODO(b/290385712): Implement. +bool BluetoothAdapter::SetScanMode(api::BluetoothAdapter::ScanMode scan_mode) { + return false; +} + +// TODO(b/290385712): Implement. +std::string BluetoothAdapter::GetName() const { + return ""; +} + +// TODO(b/290385712): Implement. +bool BluetoothAdapter::SetName(absl::string_view name) { + return false; +} + +// TODO(b/290385712): Implement. +bool BluetoothAdapter::SetName(absl::string_view name, bool persist) { + return false; +} + +// TODO(b/290385712): Implement. +std::string BluetoothAdapter::GetMacAddress() const { + return ""; +} + +} // namespace apple +} // namespace nearby From 749d5282065e900bbfed04a8127389cf2699c956 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Fri, 28 Jul 2023 13:19:56 -0700 Subject: [PATCH 033/128] Add FinishRetroactivePairing Add the second step of retroactive pairing. The client should: * start retroactive pairing * get user consent * finish retroactive pairing PiperOrigin-RevId: 551934024 --- fastpair/fast_pair_seeker.h | 23 ++++++ fastpair/fast_pair_service.cc | 5 +- fastpair/internal/BUILD | 3 +- fastpair/internal/fast_pair_seeker_impl.cc | 45 ++++++++++- fastpair/internal/fast_pair_seeker_impl.h | 9 ++- .../internal/fast_pair_seeker_impl_test.cc | 80 +++++++++++++++++-- fastpair/mock_fast_pair_seeker.h | 5 ++ 7 files changed, 156 insertions(+), 14 deletions(-) diff --git a/fastpair/fast_pair_seeker.h b/fastpair/fast_pair_seeker.h index bc9677d9..38458750 100644 --- a/fastpair/fast_pair_seeker.h +++ b/fastpair/fast_pair_seeker.h @@ -38,6 +38,13 @@ struct SubsequentPairingParam {}; // Retroactive Pairing parameters. struct RetroactivePairingParam {}; +// Finish Retroactive Pairing parameters. +struct FinishRetroactivePairingParam { + // Save the negotiated Account Key or abandon it and forget the Fast Pair + // Device. + bool save_account_key = false; +}; + // Fast Pair Seeker API available to plugins. class FastPairSeeker { public: @@ -67,12 +74,28 @@ class FastPairSeeker { // when the user has paired manually with a new device and we want to // retroactively exchange an Account Key. See: // https://developers.google.com/nearby/fast-pair/specifications/extensions/retroactiveacctkey + // The retroactive pairing flow should be started immediately after the + // pairing event, before we had a chance to ask for the user for permission. + // Therefore, the retroactive is split into two stages. The client should call + // `StartRetroactivePairing()` first. When the pairing result returned via + // `callback` is successful, the client should ask the user for permission to + // save the account key. Finally, the client should call + // `FinishRetroactivePairing()` with the user's decision to complete the + // retroactive pairing flow. // // Returns an error if pairing flow could not be started. Otherwise, the // pairing result will be returned via the `callback`. virtual absl::Status StartRetroactivePairing( const FastPairDevice& device, const RetroactivePairingParam& param, PairingCallback callback) = 0; + + // Finishes the retroactive pairing flow. + // + // Returns an error if the flow could not be started. Otherwise, the + // result will be returned via the `callback`. + virtual absl::Status FinishRetroactivePairing( + const FastPairDevice& device, const FinishRetroactivePairingParam& param, + PairingCallback callback) = 0; }; } // namespace fastpair } // namespace nearby diff --git a/fastpair/fast_pair_service.cc b/fastpair/fast_pair_service.cc index e511ce2a..87eb2dde 100644 --- a/fastpair/fast_pair_service.cc +++ b/fastpair/fast_pair_service.cc @@ -23,9 +23,9 @@ #include "fastpair/common/fast_pair_prefs.h" #include "fastpair/fast_pair_plugin.h" #include "fastpair/internal/fast_pair_seeker_impl.h" +#include "fastpair/repository/fast_pair_repository_impl.h" #include "fastpair/server_access/fast_pair_client_impl.h" #include "fastpair/server_access/fast_pair_http_notifier.h" -#include "fastpair/repository/fast_pair_repository_impl.h" #include "internal/account/account_manager_impl.h" #include "internal/auth/authentication_manager_impl.h" #include "internal/flags/nearby_flags.h" @@ -107,7 +107,8 @@ FastPairService::FastPairService( [this](const FastPairDevice& device, RingEvent event) { OnRingEvent(device, std::move(event)); }}, - &executor_, account_manager_.get(), &devices_); + &executor_, account_manager_.get(), &devices_, + fast_pair_repository_.get()); } FastPairService::~FastPairService() { executor_.Shutdown(); } diff --git a/fastpair/internal/BUILD b/fastpair/internal/BUILD index 4ffade7c..cbec33b4 100644 --- a/fastpair/internal/BUILD +++ b/fastpair/internal/BUILD @@ -16,10 +16,10 @@ cc_library( "//fastpair:fast_pair_seeker", "//fastpair/internal/mediums", "//fastpair/pairing", + "//fastpair/repository", "//fastpair/repository:device_repository", "//fastpair/retroactive", "//fastpair/scanning:scanner", - "//internal/account", "//internal/platform:types", "@com_google_absl//absl/status", "@com_google_absl//absl/strings:str_format", @@ -35,6 +35,7 @@ cc_test( deps = [ ":internal", "//fastpair:fast_pair_events", + "//fastpair:fast_pair_seeker", "//fastpair/common", "//fastpair/message_stream:fake_gatt_callbacks", "//fastpair/message_stream:fake_provider", diff --git a/fastpair/internal/fast_pair_seeker_impl.cc b/fastpair/internal/fast_pair_seeker_impl.cc index 6bb0ef3b..41c1e9ad 100644 --- a/fastpair/internal/fast_pair_seeker_impl.cc +++ b/fastpair/internal/fast_pair_seeker_impl.cc @@ -39,11 +39,13 @@ constexpr absl::Duration kCleanupTimeout = absl::Seconds(3); FastPairSeekerImpl::FastPairSeekerImpl(ServiceCallbacks callbacks, SingleThreadExecutor* executor, AccountManager* account_manager, - FastPairDeviceRepository* devices) + FastPairDeviceRepository* devices, + FastPairRepository* repository) : callbacks_(std::move(callbacks)), executor_(executor), account_manager_(account_manager), - devices_(devices) { + devices_(devices), + repository_(repository) { pairer_broker_ = std::make_unique(mediums_, executor_, account_manager_); pairer_broker_->AddObserver(this); @@ -104,6 +106,10 @@ absl::Status FastPairSeekerImpl::StartRetroactivePairing( pairing_callback_ = std::make_unique(std::move(callback)); retroactive_pair_->Pair().AddListener( [this](ExceptionOr result) { + // TODO(jsobczak): We could/should keep controller_ alive until the + // client calls FinishRetroactivePairing(), verify that they called + // Finish for the correct device, and if the user has given consent, we + // should keep the MessageStream connection open. retroactive_pair_.reset(); controller_.reset(); FinishPairing(result.result()); @@ -112,6 +118,41 @@ absl::Status FastPairSeekerImpl::StartRetroactivePairing( return absl::OkStatus(); } +absl::Status FastPairSeekerImpl::FinishRetroactivePairing( + const FastPairDevice& device, const FinishRetroactivePairingParam& param, + PairingCallback callback) { + if (device.GetProtocol() != Protocol::kFastPairRetroactivePairing) { + return absl::InvalidArgumentError( + "Fast Pair Device is not a retroactive pairing device"); + } + if (!device.GetAccountKey()) { + return absl::InvalidArgumentError( + "Fast Pair Device does not have an account key"); + } + if (!param.save_account_key) { + executor_->Execute( + "abandon retro device", + [this, &device, callback = std::move(callback)]() mutable { + NEARBY_LOGS(INFO) << "Abandon device on retroactive pairing path"; + callback.on_pairing_result(device, absl::OkStatus()); + devices_->RemoveDevice(&device); + }); + } else { + executor_->Execute( + "save account key", + [this, &device, callback = std::move(callback)]() mutable { + NEARBY_LOGS(INFO) << "Save account key for " << device; + repository_->WriteAccountAssociationToFootprints( + const_cast(device), + [&device, + callback = std::move(callback)](absl::Status status) mutable { + callback.on_pairing_result(device, status); + }); + }); + } + return absl::OkStatus(); +} + absl::Status FastPairSeekerImpl::StartFastPairScan() { if (scanning_session_ != nullptr) { return absl::AlreadyExistsError("already scanning"); diff --git a/fastpair/internal/fast_pair_seeker_impl.h b/fastpair/internal/fast_pair_seeker_impl.h index a281c545..8984b22e 100644 --- a/fastpair/internal/fast_pair_seeker_impl.h +++ b/fastpair/internal/fast_pair_seeker_impl.h @@ -25,6 +25,7 @@ #include "fastpair/internal/mediums/mediums.h" #include "fastpair/pairing/pairer_broker_impl.h" #include "fastpair/repository/fast_pair_device_repository.h" +#include "fastpair/repository/fast_pair_repository.h" #include "fastpair/retroactive/retroactive.h" #include "fastpair/retroactive/retroactive_pairing_detector_impl.h" #include "fastpair/scanning/scanner_broker_impl.h" @@ -64,7 +65,8 @@ class FastPairSeekerImpl : public FastPairSeekerExt, FastPairSeekerImpl(ServiceCallbacks callbacks, SingleThreadExecutor* executor, AccountManager* account_manager, - FastPairDeviceRepository* devices); + FastPairDeviceRepository* devices, + FastPairRepository* repository); ~FastPairSeekerImpl() override; @@ -81,6 +83,10 @@ class FastPairSeekerImpl : public FastPairSeekerExt, const RetroactivePairingParam& param, PairingCallback callback) override; + absl::Status FinishRetroactivePairing( + const FastPairDevice& device, const FinishRetroactivePairingParam& param, + PairingCallback callback) override; + // From FastPairSeekerExt. absl::Status StartFastPairScan() override; absl::Status StopFastPairScan() override; @@ -120,6 +126,7 @@ class FastPairSeekerImpl : public FastPairSeekerExt, SingleThreadExecutor* executor_; AccountManager* account_manager_; FastPairDeviceRepository* devices_; + FastPairRepository* repository_; Mediums mediums_; std::unique_ptr scanner_; std::unique_ptr scanning_session_; diff --git a/fastpair/internal/fast_pair_seeker_impl_test.cc b/fastpair/internal/fast_pair_seeker_impl_test.cc index a48ba0fd..d77c6cf4 100644 --- a/fastpair/internal/fast_pair_seeker_impl_test.cc +++ b/fastpair/internal/fast_pair_seeker_impl_test.cc @@ -26,6 +26,7 @@ #include "absl/time/time.h" #include "fastpair/common/fast_pair_prefs.h" #include "fastpair/fast_pair_events.h" +#include "fastpair/fast_pair_seeker.h" #include "fastpair/message_stream/fake_gatt_callbacks.h" #include "fastpair/message_stream/fake_provider.h" #include "fastpair/repository/fake_fast_pair_repository.h" @@ -85,6 +86,12 @@ class FastPairSeekerImplTest : public testing::Test { void TearDown() override { executor_.Shutdown(); } + void WaitForBackgroundTasks() { + CountDownLatch latch(1); + executor_.Execute([&]() { latch.CountDown(); }); + latch.Await(); + } + MediumEnvironmentStarter env_; SingleThreadExecutor executor_; std::unique_ptr preferences_manager_; @@ -100,7 +107,7 @@ class FastPairSeekerImplTest : public testing::Test { TEST_F(FastPairSeekerImplTest, StartAndStopFastPairScan) { fast_pair_seeker_ = std::make_unique( FastPairSeekerImpl::ServiceCallbacks{}, &executor_, - account_manager_.get(), &devices_); + account_manager_.get(), &devices_, repository_.get()); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); EXPECT_OK(fast_pair_seeker_->StopFastPairScan()); @@ -116,7 +123,7 @@ TEST_F(FastPairSeekerImplTest, DiscoverDevice) { EXPECT_EQ(device.GetModelId(), kModelId); latch.CountDown(); }}, - &executor_, account_manager_.get(), &devices_); + &executor_, account_manager_.get(), &devices_, repository_.get()); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); provider.StartDiscoverableAdvertisement(kModelId); @@ -128,7 +135,7 @@ TEST_F(FastPairSeekerImplTest, DiscoverDevice) { TEST_F(FastPairSeekerImplTest, StartFastPairScanTwiceFails) { fast_pair_seeker_ = std::make_unique( FastPairSeekerImpl::ServiceCallbacks{}, &executor_, - account_manager_.get(), &devices_); + account_manager_.get(), &devices_, repository_.get()); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); EXPECT_THAT(fast_pair_seeker_->StartFastPairScan(), @@ -138,7 +145,7 @@ TEST_F(FastPairSeekerImplTest, StartFastPairScanTwiceFails) { TEST_F(FastPairSeekerImplTest, StopFastPairScanTwiceFails) { fast_pair_seeker_ = std::make_unique( FastPairSeekerImpl::ServiceCallbacks{}, &executor_, - account_manager_.get(), &devices_); + account_manager_.get(), &devices_, repository_.get()); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); EXPECT_OK(fast_pair_seeker_->StopFastPairScan()); @@ -159,7 +166,7 @@ TEST_F(FastPairSeekerImplTest, ScreenLocksDuringAdvertising) { EXPECT_TRUE(event.is_locked); latch.CountDown(); }}, - &executor_, account_manager_.get(), &devices_); + &executor_, account_manager_.get(), &devices_, repository_.get()); // Create Advertiser and startAdvertising Mediums mediums_2; std::string service_id(kServiceID); @@ -195,7 +202,7 @@ TEST_F(FastPairSeekerImplTest, InitialPairing) { }})); discover_latch.CountDown(); }}, - &executor_, account_manager_.get(), &devices_); + &executor_, account_manager_.get(), &devices_, repository_.get()); EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); provider.PrepareForInitialPairing( @@ -215,7 +222,7 @@ TEST_F(FastPairSeekerImplTest, InitialPairing) { fast_pair_seeker_.reset(); } -TEST_F(FastPairSeekerImplTest, RetroactivePairing) { +TEST_F(FastPairSeekerImplTest, RetroactivePairingWithUserConsent) { NEARBY_LOG_SET_SEVERITY(VERBOSE); FakeProvider provider; CountDownLatch pair_latch(1); @@ -234,7 +241,7 @@ TEST_F(FastPairSeekerImplTest, RetroactivePairing) { retro_latch.CountDown(); }})); }}, - &executor_, account_manager_.get(), &devices_); + &executor_, account_manager_.get(), &devices_, repository_.get()); provider.PrepareForRetroactivePairing( {.private_key = absl::HexStringToBytes(kBobPrivateKey), @@ -247,6 +254,63 @@ TEST_F(FastPairSeekerImplTest, RetroactivePairing) { auto fp_device = devices_.FindDevice(provider.GetMacAddress()); ASSERT_TRUE(fp_device.has_value()); EXPECT_EQ(provider.GetAccountKey(), fp_device.value()->GetAccountKey()); + + // The client asks the user for consent, the user grants it. + CountDownLatch finish_latch(1); + EXPECT_OK(fast_pair_seeker_->FinishRetroactivePairing( + **fp_device, FinishRetroactivePairingParam{.save_account_key = true}, + {.on_pairing_result = [&](const FastPairDevice&, absl::Status status) { + EXPECT_OK(status); + finish_latch.CountDown(); + }})); + EXPECT_TRUE(finish_latch.Await()); +} + +TEST_F(FastPairSeekerImplTest, RetroactivePairingNoUserConsent) { + NEARBY_LOG_SET_SEVERITY(VERBOSE); + FakeProvider provider; + CountDownLatch pair_latch(1); + CountDownLatch retro_latch(1); + fast_pair_seeker_ = std::make_unique( + FastPairSeekerImpl::ServiceCallbacks{ + .on_pair_event = + [&](const FastPairDevice& device, PairEvent event) { + NEARBY_LOGS(INFO) << "Pair callback"; + pair_latch.CountDown(); + EXPECT_OK(fast_pair_seeker_->StartRetroactivePairing( + device, RetroactivePairingParam{}, + {.on_pairing_result = [&](const FastPairDevice&, + absl::Status status) { + EXPECT_OK(status); + retro_latch.CountDown(); + }})); + }}, + &executor_, account_manager_.get(), &devices_, repository_.get()); + + provider.PrepareForRetroactivePairing( + {.private_key = absl::HexStringToBytes(kBobPrivateKey), + .public_key = absl::HexStringToBytes(kBobPublicKey), + .model_id = std::string(kModelId)}, + &fake_gatt_callbacks_); + + EXPECT_TRUE(pair_latch.Await().Ok()); + EXPECT_TRUE(retro_latch.Await().Ok()); + auto fp_device = devices_.FindDevice(provider.GetMacAddress()); + ASSERT_TRUE(fp_device.has_value()); + EXPECT_EQ(provider.GetAccountKey(), fp_device.value()->GetAccountKey()); + + // The client asks the user for consent, the user rejects it. + CountDownLatch finish_latch(1); + EXPECT_OK(fast_pair_seeker_->FinishRetroactivePairing( + **fp_device, FinishRetroactivePairingParam{.save_account_key = false}, + {.on_pairing_result = [&](const FastPairDevice&, absl::Status status) { + EXPECT_OK(status); + finish_latch.CountDown(); + }})); + EXPECT_TRUE(finish_latch.Await()); + // The device should be deleted. + WaitForBackgroundTasks(); + EXPECT_FALSE(devices_.FindDevice(provider.GetMacAddress()).has_value()); } } // namespace diff --git a/fastpair/mock_fast_pair_seeker.h b/fastpair/mock_fast_pair_seeker.h index cd9b19f7..4bc4badd 100644 --- a/fastpair/mock_fast_pair_seeker.h +++ b/fastpair/mock_fast_pair_seeker.h @@ -38,6 +38,11 @@ class MockFastPairSeeker : public FastPairSeeker { (const FastPairDevice& device, const RetroactivePairingParam& param, PairingCallback callback), (override)); + MOCK_METHOD(absl::Status, FinishRetroactivePairing, + (const FastPairDevice& device, + const FinishRetroactivePairingParam& param, + PairingCallback callback), + (override)); }; } // namespace fastpair From e36b26184ab50fe57bc1bbc30acdc6a24d99eeb3 Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Fri, 28 Jul 2023 17:03:46 -0700 Subject: [PATCH 034/128] Retroactive pairing detector: check login status and if device has already been saved to the user's account PiperOrigin-RevId: 551985769 --- fastpair/internal/fast_pair_seeker_impl.cc | 2 +- .../internal/fast_pair_seeker_impl_test.cc | 4 +- fastpair/retroactive/BUILD | 2 + .../retroactive_pairing_detector_impl.cc | 37 +++++++++++++++---- .../retroactive_pairing_detector_impl.h | 8 ++-- 5 files changed, 40 insertions(+), 13 deletions(-) diff --git a/fastpair/internal/fast_pair_seeker_impl.cc b/fastpair/internal/fast_pair_seeker_impl.cc index 41c1e9ad..d0496bd4 100644 --- a/fastpair/internal/fast_pair_seeker_impl.cc +++ b/fastpair/internal/fast_pair_seeker_impl.cc @@ -51,7 +51,7 @@ FastPairSeekerImpl::FastPairSeekerImpl(ServiceCallbacks callbacks, pairer_broker_->AddObserver(this); mediums_.GetBluetoothClassic().AddObserver(this); retro_detector_ = std::make_unique( - mediums_, devices, executor); + mediums_, devices, account_manager_, executor); retro_detector_->AddObserver(this); } diff --git a/fastpair/internal/fast_pair_seeker_impl_test.cc b/fastpair/internal/fast_pair_seeker_impl_test.cc index d77c6cf4..9957265c 100644 --- a/fastpair/internal/fast_pair_seeker_impl_test.cc +++ b/fastpair/internal/fast_pair_seeker_impl_test.cc @@ -22,8 +22,6 @@ #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" #include "fastpair/common/fast_pair_prefs.h" #include "fastpair/fast_pair_events.h" #include "fastpair/fast_pair_seeker.h" @@ -76,6 +74,8 @@ class FastPairSeekerImplTest : public testing::Test { NEARBY_LOG_SET_SEVERITY(VERBOSE); repository_ = FakeFastPairRepository::Create( kModelId, absl::HexStringToBytes(kBobPublicKey)); + repository_->SetResultOfIsDeviceSavedToAccount( + absl::NotFoundError("not found")); account_manager_ = std::make_unique( preferences_manager_.get(), prefs::kNearbyFastPairUsersName, authentication_manager_.get(), task_runner_.get()); diff --git a/fastpair/retroactive/BUILD b/fastpair/retroactive/BUILD index ec1f012c..33884bf9 100644 --- a/fastpair/retroactive/BUILD +++ b/fastpair/retroactive/BUILD @@ -36,7 +36,9 @@ cc_library( "//fastpair/internal/mediums", "//fastpair/message_stream", "//fastpair/pairing", + "//fastpair/repository", "//fastpair/repository:device_repository", + "//internal/account", "//internal/base", "//internal/platform:comm", "//internal/platform:types", diff --git a/fastpair/retroactive/retroactive_pairing_detector_impl.cc b/fastpair/retroactive/retroactive_pairing_detector_impl.cc index 4aae90c5..7a104b94 100644 --- a/fastpair/retroactive/retroactive_pairing_detector_impl.cc +++ b/fastpair/retroactive/retroactive_pairing_detector_impl.cc @@ -17,18 +17,23 @@ #include #include #include +#include #include #include "fastpair/internal/mediums/mediums.h" -#include "fastpair/pairing/pairer_broker.h" +#include "fastpair/repository/fast_pair_repository.h" +#include "internal/account/account_manager.h" namespace nearby { namespace fastpair { RetroactivePairingDetectorImpl::RetroactivePairingDetectorImpl( Mediums& mediums, FastPairDeviceRepository* repository, - SingleThreadExecutor* executor) - : mediums_(mediums), repository_(repository), executor_(executor) { + AccountManager* account_manager, SingleThreadExecutor* executor) + : mediums_(mediums), + repository_(repository), + account_manager_(account_manager), + executor_(executor) { mediums_.GetBluetoothClassic().AddObserver(this); mediums_.GetBluetoothClassic().StartDiscovery(); } @@ -75,15 +80,33 @@ void RetroactivePairingDetectorImpl::DevicePairedChanged( // first check if it has already been saved to the user's account. If it has // already been saved, we don't want to prompt the user to save a device // again. - // TODO(b/285047010): check if device has already been saved to the user's - // account + if (!account_manager_->GetCurrentAccount().has_value()) { + NEARBY_LOGS(INFO) << __func__ << ": Ignoring because no logged in user."; + return; + } + FastPairRepository::Get()->IsDeviceSavedToAccount( + device.GetMacAddress(), + [this, mac_address = device.GetMacAddress()](absl::Status status) { + if (status.ok()) { + NEARBY_LOGS(VERBOSE) << __func__ + << ": Ignoring because device is already saved " + "to the current account."; + return; + } + NotifyRetroactiveDeviceFound(mac_address); + }); +} + +void RetroactivePairingDetectorImpl::NotifyRetroactiveDeviceFound( + absl::string_view mac_address) { + NEARBY_LOGS(VERBOSE) << __func__ << ": mac_address = " << mac_address; auto fast_pair_device = std::make_unique(Protocol::kFastPairRetroactivePairing); - fast_pair_device->SetPublicAddress(device.GetMacAddress()); + fast_pair_device->SetPublicAddress(mac_address); repository_->AddDevice(std::move(fast_pair_device)); executor_->Execute("notify-retro-candidate", - [this, address = device.GetMacAddress()]() { + [this, address = std::string(mac_address)]() { std::optional fast_pair_device = repository_->FindDevice(address); if (!fast_pair_device) return; diff --git a/fastpair/retroactive/retroactive_pairing_detector_impl.h b/fastpair/retroactive/retroactive_pairing_detector_impl.h index a24d0617..7c876f86 100644 --- a/fastpair/retroactive/retroactive_pairing_detector_impl.h +++ b/fastpair/retroactive/retroactive_pairing_detector_impl.h @@ -14,13 +14,12 @@ #ifndef THIRD_PARTY_NEARBY_FASTPAIR_RETROACTIVE_RETROACTIVE_PAIRING_DETECTOR_IMPL_H_ #define THIRD_PARTY_NEARBY_FASTPAIR_RETROACTIVE_RETROACTIVE_PAIRING_DETECTOR_IMPL_H_ -#include -#include "absl/container/flat_hash_set.h" +#include "absl/strings/string_view.h" #include "fastpair/internal/mediums/mediums.h" -#include "fastpair/pairing/pairer_broker.h" #include "fastpair/repository/fast_pair_device_repository.h" #include "fastpair/retroactive/retroactive_pairing_detector.h" +#include "internal/account/account_manager.h" #include "internal/base/observer_list.h" #include "internal/platform/bluetooth_classic.h" #include "internal/platform/single_thread_executor.h" @@ -34,6 +33,7 @@ class RetroactivePairingDetectorImpl public: RetroactivePairingDetectorImpl(Mediums& mediums, FastPairDeviceRepository* repository, + AccountManager* account_manager, SingleThreadExecutor* executor); RetroactivePairingDetectorImpl(const RetroactivePairingDetectorImpl&) = delete; @@ -50,9 +50,11 @@ class RetroactivePairingDetectorImpl bool new_paired_status) override; private: + void NotifyRetroactiveDeviceFound(absl::string_view mac_address); Mediums& mediums_; ObserverList observers_; FastPairDeviceRepository* repository_; + AccountManager* account_manager_; SingleThreadExecutor* executor_; }; From 494eb660271f050b31f17e67940b341e7d3e5290 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 18 Jul 2023 15:18:53 -0700 Subject: [PATCH 035/128] [fp-rs] Implemented custom error types for Bluetooth library. --- fastpair/rust/Cargo.toml | 2 +- fastpair/rust/src/bluetooth/common/adapter.rs | 10 ++--- fastpair/rust/src/bluetooth/common/device.rs | 4 +- fastpair/rust/src/bluetooth/common/error.rs | 42 +++++++++++++++++++ fastpair/rust/src/bluetooth/common/mod.rs | 2 + fastpair/rust/src/bluetooth/mod.rs | 4 +- .../rust/src/bluetooth/unsupported/adapter.rs | 10 ++--- .../rust/src/bluetooth/unsupported/device.rs | 4 +- .../rust/src/bluetooth/windows/adapter.rs | 34 ++++++++------- fastpair/rust/src/bluetooth/windows/device.rs | 6 +-- fastpair/rust/src/bluetooth/windows/error.rs | 21 ++++++++++ fastpair/rust/src/bluetooth/windows/mod.rs | 1 + fastpair/rust/src/main.rs | 4 +- 13 files changed, 110 insertions(+), 34 deletions(-) create mode 100644 fastpair/rust/src/bluetooth/common/error.rs create mode 100644 fastpair/rust/src/bluetooth/windows/error.rs diff --git a/fastpair/rust/Cargo.toml b/fastpair/rust/Cargo.toml index 20a09107..02a7b908 100644 --- a/fastpair/rust/Cargo.toml +++ b/fastpair/rust/Cargo.toml @@ -20,11 +20,11 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "1.0" futures = { version = "0.3", features = ["executor"] } tracing = "0.1.37" cfg-if = "1.0.0" async-trait = "0.1" +thiserror = "1.0.43" [target.'cfg(windows)'.dependencies] windows = { version = "0.48", features = [ diff --git a/fastpair/rust/src/bluetooth/common/adapter.rs b/fastpair/rust/src/bluetooth/common/adapter.rs index 5e8be0ae..40a504ee 100644 --- a/fastpair/rust/src/bluetooth/common/adapter.rs +++ b/fastpair/rust/src/bluetooth/common/adapter.rs @@ -14,7 +14,7 @@ use async_trait::async_trait; -use super::Device; +use super::{BluetoothError, Device}; /// Concrete types implementing this trait are Bluetooth Central devices. /// They provide methods for retrieving nearby connections and device info. @@ -23,14 +23,14 @@ pub trait Adapter: Sized { type Device: Device; /// Retrieve the system-default Bluetooth adapter. - async fn default() -> Result; + async fn default() -> Result; /// Begin scanning for nearby devices. - fn start_scan_devices(&mut self) -> Result<(), anyhow::Error>; + fn start_scan_devices(&mut self) -> Result<(), BluetoothError>; /// Stop scanning for nearby devices. - fn stop_scan_devices(&mut self) -> Result<(), anyhow::Error>; + fn stop_scan_devices(&mut self) -> Result<(), BluetoothError>; /// Poll next discovered device. - async fn next_device(&mut self) -> Result; + async fn next_device(&mut self) -> Result; } diff --git a/fastpair/rust/src/bluetooth/common/device.rs b/fastpair/rust/src/bluetooth/common/device.rs index 1849fcef..33ef11c2 100644 --- a/fastpair/rust/src/bluetooth/common/device.rs +++ b/fastpair/rust/src/bluetooth/common/device.rs @@ -15,7 +15,9 @@ /// Concrete types implementing this trait represent Bluetooth Peripheral devices. /// They provide methods for retrieving device info and running device actions, /// such as pairing. +use super::BluetoothError; + pub trait Device { /// Retrieve the name advertised by this device. - fn name(&self) -> Result; + fn name(&self) -> Result; } diff --git a/fastpair/rust/src/bluetooth/common/error.rs b/fastpair/rust/src/bluetooth/common/error.rs new file mode 100644 index 00000000..3e7ac640 --- /dev/null +++ b/fastpair/rust/src/bluetooth/common/error.rs @@ -0,0 +1,42 @@ +// 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. + +use thiserror::Error; + +/// Library error type. +#[non_exhaustive] +#[derive(Error, Debug)] +pub enum BluetoothError { + /// Indicates that the operation was rejected because the system is not in + /// a state required for the operation's execution. + /// E.g. The user calls `stop_scan()` or polls the advertisement stream + /// before calling `start_scan()`. + #[error("failed precondition: {0}")] + FailedPrecondition(String), + /// Reported when the user calls an operation that is supported by their + /// Operating System, but is not supported by their device. + /// E.g. a Windows machine with an old BT Classic adapter that + /// doesn't support BLE). + #[error("bluetooth operation not supported by system: {0}")] + NotSupported(String), + /// Wrapper around OS-level errors, e.g. `windows::core::Error` for Windows. + /// These typically mean something is very wrong with the system (e.g. OOM). + #[error("bluetooth system-level error: {0}")] + System(String), + /// Reported when a bug occurs inside the library. Whenever a seemingly + /// impossible error condition arises where you could call `expect()`, + /// return this error instead. + #[error("internal error: {0}")] + Internal(String), +} diff --git a/fastpair/rust/src/bluetooth/common/mod.rs b/fastpair/rust/src/bluetooth/common/mod.rs index ea2fa401..d0fce7c4 100644 --- a/fastpair/rust/src/bluetooth/common/mod.rs +++ b/fastpair/rust/src/bluetooth/common/mod.rs @@ -15,6 +15,8 @@ /// Module for shared functionality between all Bluetooth platforms. mod adapter; mod device; +mod error; pub use adapter::*; pub use device::*; +pub use error::*; diff --git a/fastpair/rust/src/bluetooth/mod.rs b/fastpair/rust/src/bluetooth/mod.rs index b49b95df..765086f2 100644 --- a/fastpair/rust/src/bluetooth/mod.rs +++ b/fastpair/rust/src/bluetooth/mod.rs @@ -18,7 +18,7 @@ pub mod common; -pub use common::{Adapter, Device}; +pub use common::{Adapter, BluetoothError, Device}; cfg_if::cfg_if! { if #[cfg(windows)] { @@ -30,6 +30,6 @@ cfg_if::cfg_if! { } } -pub async fn default_adapter() -> Result { +pub async fn default_adapter() -> Result { BleAdapter::default().await } diff --git a/fastpair/rust/src/bluetooth/unsupported/adapter.rs b/fastpair/rust/src/bluetooth/unsupported/adapter.rs index f7395ea5..8baa3b81 100644 --- a/fastpair/rust/src/bluetooth/unsupported/adapter.rs +++ b/fastpair/rust/src/bluetooth/unsupported/adapter.rs @@ -15,7 +15,7 @@ use async_trait::async_trait; use super::BleDevice; -use crate::bluetooth::common::Adapter; +use crate::bluetooth::common::{Adapter, BluetoothError}; /// Concrete type implementing `Adapter`, used for unsupported devices. /// Every method should panic. @@ -25,19 +25,19 @@ pub struct BleAdapter; impl Adapter for BleAdapter { type Device = BleDevice; - async fn default() -> Result { + async fn default() -> Result { panic!("Unsupported target platform."); } - fn start_scan_devices(&mut self) -> Result<(), anyhow::Error> { + fn start_scan_devices(&mut self) -> Result<(), BluetoothError> { panic!("Unsupported target platform."); } - fn stop_scan_devices(&mut self) -> Result<(), anyhow::Error> { + fn stop_scan_devices(&mut self) -> Result<(), BluetoothError> { panic!("Unsupported target platform."); } - async fn next_device(&mut self) -> Result { + async fn next_device(&mut self) -> Result { panic!("Unsupported target platform."); } } diff --git a/fastpair/rust/src/bluetooth/unsupported/device.rs b/fastpair/rust/src/bluetooth/unsupported/device.rs index 6c8bc520..d18d1e34 100644 --- a/fastpair/rust/src/bluetooth/unsupported/device.rs +++ b/fastpair/rust/src/bluetooth/unsupported/device.rs @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bluetooth::common::Device; +use crate::bluetooth::common::{BluetoothError, Device}; /// Concrete type implementing `Device`, used for unsupported devices. /// Every method should panic. pub struct BleDevice; impl Device for BleDevice { - fn name(&self) -> Result { + fn name(&self) -> Result { panic!("Unsupported target platform.") } } diff --git a/fastpair/rust/src/bluetooth/windows/adapter.rs b/fastpair/rust/src/bluetooth/windows/adapter.rs index 6d7af121..0cabf752 100644 --- a/fastpair/rust/src/bluetooth/windows/adapter.rs +++ b/fastpair/rust/src/bluetooth/windows/adapter.rs @@ -55,7 +55,7 @@ use windows::{ }; use super::BleDevice; -use crate::bluetooth::common::Adapter; +use crate::bluetooth::common::{Adapter, BluetoothError}; /// Concrete type implementing `Adapter`, used for Windows BLE. pub struct BleAdapter { @@ -71,18 +71,18 @@ pub struct BleAdapter { impl Adapter for BleAdapter { type Device = BleDevice; - async fn default() -> Result { + async fn default() -> Result { let inner = BluetoothAdapter::GetDefaultAsync()?.await?; if !inner.IsLowEnergySupported()? { - return Err(anyhow::anyhow!( - "This device's Bluetooth Adapter doesn't support Bluetooth LE Transport type." - )); + return Err(BluetoothError::NotSupported(String::from( + "LE transport type", + ))); } if !inner.IsCentralRoleSupported()? { - return Err(anyhow::anyhow!( - "This device's Bluetooth Adapter doesn't support Bluetooth LE central role." - )); + return Err(BluetoothError::NotSupported(String::from( + "central role", + ))); } Ok(BleAdapter { @@ -91,7 +91,7 @@ impl Adapter for BleAdapter { }) } - fn start_scan_devices(&mut self) -> Result<(), anyhow::Error> { + fn start_scan_devices(&mut self) -> Result<(), BluetoothError> { let watcher = BluetoothLEAdvertisementWatcher::new()?; match watcher.SetScanningMode(BluetoothLEScanningMode::Active) { Ok(_) => (), @@ -194,23 +194,29 @@ impl Adapter for BleAdapter { Ok(()) } - fn stop_scan_devices(&mut self) -> Result<(), anyhow::Error> { + fn stop_scan_devices(&mut self) -> Result<(), BluetoothError> { if let Some(_) = &self.device_stream { self.device_stream.take(); Ok(()) } else { - Err(anyhow::anyhow!("Device scanning hasn't started.")) + Err(BluetoothError::FailedPrecondition(String::from( + "device scanning hasn't started, please call `start_scan()`", + ))) } } - async fn next_device(&mut self) -> Result { + async fn next_device(&mut self) -> Result { if let Some(stream) = &mut self.device_stream { stream .next() .await - .ok_or(anyhow::anyhow!("Device returned from stream is None.")) + .ok_or(BluetoothError::Internal(String::from( + "device returned from Stream is None", + ))) } else { - Err(anyhow::anyhow!("Device scanning hasn't started.")) + Err(BluetoothError::FailedPrecondition(String::from( + "device scanning hasn't started, please call `start_scan()`", + ))) } } } diff --git a/fastpair/rust/src/bluetooth/windows/device.rs b/fastpair/rust/src/bluetooth/windows/device.rs index 08c3d123..60bd872b 100644 --- a/fastpair/rust/src/bluetooth/windows/device.rs +++ b/fastpair/rust/src/bluetooth/windows/device.rs @@ -23,7 +23,7 @@ use windows::Devices::Bluetooth::{ BluetoothLEDevice, }; -use crate::bluetooth::common::Device; +use crate::bluetooth::common::{BluetoothError, Device}; /// Concrete type implementing `Device`, used for Windows BLE. pub struct BleDevice { @@ -35,7 +35,7 @@ impl BleDevice { pub(super) async fn from_addr( addr: u64, kind: BluetoothAddressType, - ) -> Result { + ) -> Result { let inner = BluetoothLEDevice::FromBluetoothAddressWithBluetoothAddressTypeAsync(addr, kind)? .await?; @@ -46,7 +46,7 @@ impl BleDevice { #[async_trait] impl Device for BleDevice { - fn name(&self) -> Result { + fn name(&self) -> Result { Ok(self.inner.Name()?.to_string_lossy()) } } diff --git a/fastpair/rust/src/bluetooth/windows/error.rs b/fastpair/rust/src/bluetooth/windows/error.rs new file mode 100644 index 00000000..04c6bce3 --- /dev/null +++ b/fastpair/rust/src/bluetooth/windows/error.rs @@ -0,0 +1,21 @@ +// 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. + +use crate::bluetooth::common::BluetoothError; + +impl From for BluetoothError { + fn from(err: windows::core::Error) -> Self { + BluetoothError::System(err.to_string()) + } +} diff --git a/fastpair/rust/src/bluetooth/windows/mod.rs b/fastpair/rust/src/bluetooth/windows/mod.rs index 7d54d4ef..4835654d 100644 --- a/fastpair/rust/src/bluetooth/windows/mod.rs +++ b/fastpair/rust/src/bluetooth/windows/mod.rs @@ -15,6 +15,7 @@ /// Bluetooth LE module for Windows devices. mod adapter; mod device; +mod error; pub use adapter::*; pub use device::*; diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index 5905e135..c0326ba6 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -12,13 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::error::Error; + use futures::executor; mod bluetooth; use bluetooth::{Adapter, Device}; -fn main() -> Result<(), anyhow::Error> { +fn main() -> Result<(), Box> { let run = async { let mut adapter = bluetooth::default_adapter().await?; adapter.start_scan_devices()?; From 9da0bf2987b0307641221bd35ce099a967864b92 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Wed, 12 Jul 2023 15:40:11 -0700 Subject: [PATCH 036/128] [fp-rs] Implemented address API --- fastpair/rust/src/bluetooth/common/address.rs | 100 ++++++++++++++++++ fastpair/rust/src/bluetooth/common/mod.rs | 2 + .../rust/src/bluetooth/windows/adapter.rs | 9 +- .../rust/src/bluetooth/windows/address.rs | 50 +++++++++ fastpair/rust/src/bluetooth/windows/device.rs | 18 ++-- fastpair/rust/src/bluetooth/windows/mod.rs | 2 + 6 files changed, 169 insertions(+), 12 deletions(-) create mode 100644 fastpair/rust/src/bluetooth/common/address.rs create mode 100644 fastpair/rust/src/bluetooth/windows/address.rs diff --git a/fastpair/rust/src/bluetooth/common/address.rs b/fastpair/rust/src/bluetooth/common/address.rs new file mode 100644 index 00000000..87c9139d --- /dev/null +++ b/fastpair/rust/src/bluetooth/common/address.rs @@ -0,0 +1,100 @@ +// 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. + +/// BLE Addresses can either be the peripheral's public MAC address, or various +/// types of random addresses. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub enum BleAddressKind { + Public, + Random, +} + +/// Struct representing a 48-bit BLE Address and its type. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct BleAddress { + val: [u8; 6], + kind: BleAddressKind, +} + +/// Struct representing a 48-bit BT Classic address. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ClassicAddress([u8; 6]); + +/// Enum for interfacing with Bluetooth Addresses. +pub enum Address { + Ble(BleAddress), + Classic(ClassicAddress), +} + +impl BleAddress { + /// `BleAddress` constructor. + pub fn new(addr: u64, kind: BleAddressKind) -> Self { + let addr = u64_to_6lsb(addr); + + BleAddress { val: addr, kind } + } + + /// Retrieve the type of BLE Address (public or random). + pub fn get_kind(&self) -> BleAddressKind { + self.kind + } +} + +/// Function for converting the six LSB of a u64 into a 6-byte array. +#[inline] +fn u64_to_6lsb(num: u64) -> [u8; 6] { + num.to_le_bytes()[..6] + .try_into() + .expect("Sanity check, slice length matches array length") +} + +impl From for ClassicAddress { + fn from(addr: u64) -> Self { + let addr = u64_to_6lsb(addr); + + ClassicAddress(addr) + } +} + +impl TryFrom for ClassicAddress { + // TODO proper error handling b/291931475 + type Error = anyhow::Error; + + fn try_from(addr: BleAddress) -> Result { + match addr.kind { + BleAddressKind::Public => Ok(ClassicAddress(addr.val)), + BleAddressKind::Random => Err(anyhow::anyhow!( + "Can't convert BLE Random address to Bluetooth Classic address." + )), + } + } +} + +impl From for u64 { + fn from(addr: BleAddress) -> Self { + let mut bytes = [0u8; 8]; + bytes[..6].copy_from_slice(&addr.val); + + u64::from_le_bytes(bytes) + } +} + +impl From for u64 { + fn from(addr: ClassicAddress) -> Self { + let mut bytes = [0u8; 8]; + bytes.copy_from_slice(&addr.0); + + u64::from_le_bytes(bytes) + } +} diff --git a/fastpair/rust/src/bluetooth/common/mod.rs b/fastpair/rust/src/bluetooth/common/mod.rs index d0fce7c4..f604ede6 100644 --- a/fastpair/rust/src/bluetooth/common/mod.rs +++ b/fastpair/rust/src/bluetooth/common/mod.rs @@ -14,9 +14,11 @@ /// Module for shared functionality between all Bluetooth platforms. mod adapter; +mod address; mod device; mod error; pub use adapter::*; +pub use address::*; pub use device::*; pub use error::*; diff --git a/fastpair/rust/src/bluetooth/windows/adapter.rs b/fastpair/rust/src/bluetooth/windows/adapter.rs index 0cabf752..57fb533d 100644 --- a/fastpair/rust/src/bluetooth/windows/adapter.rs +++ b/fastpair/rust/src/bluetooth/windows/adapter.rs @@ -55,7 +55,7 @@ use windows::{ }; use super::BleDevice; -use crate::bluetooth::common::{Adapter, BluetoothError}; +use crate::bluetooth::common::{Adapter, BleAddress, BleAddressKind, BluetoothError}; /// Concrete type implementing `Adapter`, used for Windows BLE. pub struct BleAdapter { @@ -176,10 +176,13 @@ impl Adapter for BleAdapter { None } _ => { - let addr = event_args.BluetoothAddress().ok()?; let kind = event_args.BluetoothAddressType().ok()?; + let addr = event_args.BluetoothAddress().ok()?; - match BleDevice::from_addr(addr, kind).await { + let kind = BleAddressKind::try_from(kind).ok()?; + let addr = BleAddress::new(addr, kind); + + match BleDevice::new(addr).await { Ok(device) => Some(device), Err(err) => { warn!("Error creating device: {:?}", err); diff --git a/fastpair/rust/src/bluetooth/windows/address.rs b/fastpair/rust/src/bluetooth/windows/address.rs new file mode 100644 index 00000000..6f587237 --- /dev/null +++ b/fastpair/rust/src/bluetooth/windows/address.rs @@ -0,0 +1,50 @@ +// 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. + +// Whether the Bluetooth advertisement is Public, Random or Unspecified. +//https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothaddresstype?view=winrt-22621 +use windows::Devices::Bluetooth::BluetoothAddressType; + +use crate::bluetooth::common::BleAddressKind; + +// Convenience for converting from Windows API to crate API. +impl TryFrom for BleAddressKind { + type Error = anyhow::Error; + + fn try_from(kind: BluetoothAddressType) -> Result { + match kind { + BluetoothAddressType::Public => Ok(BleAddressKind::Public), + BluetoothAddressType::Random => Ok(BleAddressKind::Random), + BluetoothAddressType::Unspecified => Err(anyhow::anyhow!( + "Attempting to construct `BleAddressKind` with device \ + advertising Unspecified address type." + )), + _ => Err(anyhow::anyhow!(format!( + "Attempting to construct `BleAddressKind` with device \ + advertising invalid address type {}.", + kind.0, + ))), + } + } +} + +// Convenience for converting from crate API to Windows API. +impl From for BluetoothAddressType { + fn from(kind: BleAddressKind) -> Self { + match kind { + BleAddressKind::Public => BluetoothAddressType::Public, + BleAddressKind::Random => BluetoothAddressType::Random, + } + } +} diff --git a/fastpair/rust/src/bluetooth/windows/device.rs b/fastpair/rust/src/bluetooth/windows/device.rs index 60bd872b..6d1a4a29 100644 --- a/fastpair/rust/src/bluetooth/windows/device.rs +++ b/fastpair/rust/src/bluetooth/windows/device.rs @@ -23,7 +23,7 @@ use windows::Devices::Bluetooth::{ BluetoothLEDevice, }; -use crate::bluetooth::common::{BluetoothError, Device}; +use crate::bluetooth::common::{BleAddress, BluetoothError, Device}; /// Concrete type implementing `Device`, used for Windows BLE. pub struct BleDevice { @@ -31,14 +31,14 @@ pub struct BleDevice { } impl BleDevice { - /// Create a `BleDevice` instance from the raw bluetooth address information. - pub(super) async fn from_addr( - addr: u64, - kind: BluetoothAddressType, - ) -> Result { - let inner = - BluetoothLEDevice::FromBluetoothAddressWithBluetoothAddressTypeAsync(addr, kind)? - .await?; + pub async fn new(addr: BleAddress) -> Result { + let kind = BluetoothAddressType::from(addr.get_kind()); + let addr = u64::from(addr); + + let inner = BluetoothLEDevice::FromBluetoothAddressWithBluetoothAddressTypeAsync( + addr, kind, + )? + .await?; Ok(BleDevice { inner }) } diff --git a/fastpair/rust/src/bluetooth/windows/mod.rs b/fastpair/rust/src/bluetooth/windows/mod.rs index 4835654d..f23035c5 100644 --- a/fastpair/rust/src/bluetooth/windows/mod.rs +++ b/fastpair/rust/src/bluetooth/windows/mod.rs @@ -14,8 +14,10 @@ /// Bluetooth LE module for Windows devices. mod adapter; +mod address; mod device; mod error; pub use adapter::*; +pub use address::*; pub use device::*; From fc07423925dfd3ad8dc98b6e34f9653108b6e2c5 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Wed, 19 Jul 2023 11:29:53 -0700 Subject: [PATCH 037/128] [fp-rs] Implemented classic device pairing --- fastpair/rust/Cargo.toml | 1 + fastpair/rust/src/bluetooth/common/address.rs | 2 +- fastpair/rust/src/bluetooth/common/device.rs | 15 +- fastpair/rust/src/bluetooth/common/error.rs | 16 +- fastpair/rust/src/bluetooth/mod.rs | 12 +- fastpair/rust/src/bluetooth/windows/device.rs | 144 ++++++++++++++++-- fastpair/rust/src/bluetooth/windows/error.rs | 38 ++++- fastpair/rust/src/main.rs | 40 ++++- 8 files changed, 242 insertions(+), 26 deletions(-) diff --git a/fastpair/rust/Cargo.toml b/fastpair/rust/Cargo.toml index 02a7b908..a8bc4428 100644 --- a/fastpair/rust/Cargo.toml +++ b/fastpair/rust/Cargo.toml @@ -29,6 +29,7 @@ thiserror = "1.0.43" [target.'cfg(windows)'.dependencies] windows = { version = "0.48", features = [ "Devices_Bluetooth", + "Devices_Enumeration", "Devices_Bluetooth_Advertisement", "Foundation", ] } diff --git a/fastpair/rust/src/bluetooth/common/address.rs b/fastpair/rust/src/bluetooth/common/address.rs index 87c9139d..dc54e067 100644 --- a/fastpair/rust/src/bluetooth/common/address.rs +++ b/fastpair/rust/src/bluetooth/common/address.rs @@ -93,7 +93,7 @@ impl From for u64 { impl From for u64 { fn from(addr: ClassicAddress) -> Self { let mut bytes = [0u8; 8]; - bytes.copy_from_slice(&addr.0); + bytes[..6].copy_from_slice(&addr.0); u64::from_le_bytes(bytes) } diff --git a/fastpair/rust/src/bluetooth/common/device.rs b/fastpair/rust/src/bluetooth/common/device.rs index 33ef11c2..a4c5756c 100644 --- a/fastpair/rust/src/bluetooth/common/device.rs +++ b/fastpair/rust/src/bluetooth/common/device.rs @@ -12,12 +12,21 @@ // See the License for the specific language governing permissions and // limitations under the License. +use async_trait::async_trait; + +use super::{Address, BluetoothError, PairingResult}; + /// Concrete types implementing this trait represent Bluetooth Peripheral devices. /// They provide methods for retrieving device info and running device actions, /// such as pairing. -use super::BluetoothError; - -pub trait Device { +#[async_trait] +pub trait Device: Sized { /// Retrieve the name advertised by this device. fn name(&self) -> Result; + + /// Retrieve this device's Bluetooth address information. + fn address(&self) -> Address; + + /// Attempt pairing with the peripheral device. + async fn pair(&self) -> Result; } diff --git a/fastpair/rust/src/bluetooth/common/error.rs b/fastpair/rust/src/bluetooth/common/error.rs index 3e7ac640..5c9e49e9 100644 --- a/fastpair/rust/src/bluetooth/common/error.rs +++ b/fastpair/rust/src/bluetooth/common/error.rs @@ -18,10 +18,12 @@ use thiserror::Error; #[non_exhaustive] #[derive(Error, Debug)] pub enum BluetoothError { + /// Reported when Bluetooth device pairing fails. + #[error("pairing error: {0}")] + PairingFailed(String), /// Indicates that the operation was rejected because the system is not in /// a state required for the operation's execution. /// E.g. The user calls `stop_scan()` or polls the advertisement stream - /// before calling `start_scan()`. #[error("failed precondition: {0}")] FailedPrecondition(String), /// Reported when the user calls an operation that is supported by their @@ -40,3 +42,15 @@ pub enum BluetoothError { #[error("internal error: {0}")] Internal(String), } + +/// Abstraction around platform-specific pairing status enums. +/// `PairingResult::Failure` should eventually be converted to +/// `BluetoothError::PairingFailed`. +#[non_exhaustive] +#[derive(Debug)] +pub enum PairingResult { + Success, + AlreadyPaired, + AlreadyInProgress, + Failure(String), +} diff --git a/fastpair/rust/src/bluetooth/mod.rs b/fastpair/rust/src/bluetooth/mod.rs index 765086f2..fb374de6 100644 --- a/fastpair/rust/src/bluetooth/mod.rs +++ b/fastpair/rust/src/bluetooth/mod.rs @@ -18,18 +18,24 @@ pub mod common; -pub use common::{Adapter, BluetoothError, Device}; +pub use common::{Adapter, Address, BluetoothError, ClassicAddress, Device}; cfg_if::cfg_if! { if #[cfg(windows)] { mod windows; - use self::windows::BleAdapter; + use self::windows::{ClassicDevice, BleAdapter}; } else { mod unsupported; - use unsupported::BleAdapter; + use unsupported::{ClassicDevice, BleAdapter}; } } pub async fn default_adapter() -> Result { BleAdapter::default().await } + +pub async fn new_classic_device( + addr: ClassicAddress, +) -> Result { + ClassicDevice::new(addr).await +} diff --git a/fastpair/rust/src/bluetooth/windows/device.rs b/fastpair/rust/src/bluetooth/windows/device.rs index 6d1a4a29..7e9f9cf0 100644 --- a/fastpair/rust/src/bluetooth/windows/device.rs +++ b/fastpair/rust/src/bluetooth/windows/device.rs @@ -13,42 +13,162 @@ // limitations under the License. use async_trait::async_trait; -use windows::Devices::Bluetooth::{ - // Enum describing the type of address (public, random, unspecified). - // https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothaddresstype?view=winrt-22621 - BluetoothAddressType, - - // Struct for interacting with and pairing to a discovered BLE device. - // https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice?view=winrt-22621 - BluetoothLEDevice, +use tracing::{info, warn}; +use windows::{ + Devices::{ + Bluetooth::{ + // Tuple struct describing the type of address (public, random, unspecified). + // https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothaddresstype?view=winrt-22621 + BluetoothAddressType, + + // Struct for interacting with a discovered BT Classic device. + // https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothdevice?view=winrt-22621 + BluetoothDevice, + + // Struct for interacting with a discovered BLE device. + // https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothledevice?view=winrt-22621 + BluetoothLEDevice, + }, + Enumeration::{ + // Struct for custom pairing with a device. + // https://learn.microsoft.com/en-us/uwp/api/windows.devices.enumeration.deviceinformationcustompairing?view=winrt-22621 + DeviceInformationCustomPairing, + + // Tuple struct to indicate the kinds of pairing supported by the application. + // https://learn.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicepairingkinds?view=winrt-22621 + DevicePairingKinds, + + // Struct for retrieving data about a PairingRequested event. + // https://learn.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicepairingrequestedeventargs?view=winrt-22621 + DevicePairingRequestedEventArgs, + }, + }, + // Wraps a closure for handling events associated with a struct + // (e.g. PairingRequested event in `DeviceInformationCustomPairing`). + // https://learn.microsoft.com/en-us/uwp/api/windows.foundation.typedeventhandler-2?view=winrt-22621 + Foundation::TypedEventHandler, }; -use crate::bluetooth::common::{BleAddress, BluetoothError, Device}; +use crate::bluetooth::common::{Address, BleAddress, ClassicAddress, Device, BluetoothError, PairingResult}; /// Concrete type implementing `Device`, used for Windows BLE. pub struct BleDevice { inner: BluetoothLEDevice, + addr: BleAddress, +} + +/// Concrete type implementing `Device`, used for Windows Bluetooth Classic. +pub struct ClassicDevice { + inner: BluetoothDevice, + addr: ClassicAddress, } impl BleDevice { + /// `BleDevice` constructor. pub async fn new(addr: BleAddress) -> Result { let kind = BluetoothAddressType::from(addr.get_kind()); - let addr = u64::from(addr); + let raw_addr = u64::from(addr); let inner = BluetoothLEDevice::FromBluetoothAddressWithBluetoothAddressTypeAsync( - addr, kind, + raw_addr, kind, )? .await?; - Ok(BleDevice { inner }) + Ok(BleDevice { inner, addr }) } } #[async_trait] impl Device for BleDevice { + fn name(&self) -> Result { + Ok(self.inner.Name()?.to_string()) + } + + fn address(&self) -> Address { + Address::Ble(self.addr) + } + + async fn pair(&self) -> Result { + // BLE Audio isn't supported on Windows natively, so devices can pair + // but don't playback. Might possibly work with UWP. Since the Classic + // and BLE APIs are very similar, it might be possible to copy-paste + // `ClassicDevice::pair` directly. + unimplemented!("BLE Pairing is currently unsupported.") + } +} + + +impl ClassicDevice { + /// `ClassicDevice` constructor. + pub async fn new(addr: ClassicAddress) -> Result { + let raw_addr = u64::from(addr); + + let inner = BluetoothDevice::FromBluetoothAddressAsync( + raw_addr, + )? + .await?; + + Ok(ClassicDevice { inner, addr }) + } +} + +#[async_trait] +impl Device for ClassicDevice { fn name(&self) -> Result { Ok(self.inner.Name()?.to_string_lossy()) } + + fn address(&self) -> Address { + Address::Classic(self.addr) + } + + async fn pair(&self) -> Result { + let pair_info = self.inner.DeviceInformation()?.Pairing()?; + if pair_info.IsPaired()? { + info!("Device already paired"); + Ok(PairingResult::AlreadyPaired) + } else if !pair_info.CanPair()? { + info!("Device can't pair"); + Err(BluetoothError::PairingFailed(String::from("device can't pair"))) + } else { + let custom = pair_info.Custom()?; + custom.PairingRequested(&TypedEventHandler::new( + |_custom: &Option, + event_args: &Option, + | { + if let Some(event_args) = event_args { + match event_args.PairingKind()? { + DevicePairingKinds::ConfirmOnly => { + event_args.Accept() + } + _ => { + warn!("Unsupported pairing kind {:?}", event_args.PairingKind()); + Ok(()) + } + } + } else { + warn!("Empty pairing event arguments"); + Ok(()) + } + + }, + ))?; + let res = custom + .PairAsync( + DevicePairingKinds::ConfirmOnly + | DevicePairingKinds::ProvidePin + | DevicePairingKinds::ConfirmPinMatch + | DevicePairingKinds::DisplayPin, + )? + .await?; + let status = PairingResult::from(res.Status()?); + + match status { + PairingResult::Failure(msg) => Err(BluetoothError::PairingFailed(msg)), + _ => Ok(status), + } + } + } } mod tests { diff --git a/fastpair/rust/src/bluetooth/windows/error.rs b/fastpair/rust/src/bluetooth/windows/error.rs index 04c6bce3..59e12a31 100644 --- a/fastpair/rust/src/bluetooth/windows/error.rs +++ b/fastpair/rust/src/bluetooth/windows/error.rs @@ -12,10 +12,46 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bluetooth::common::BluetoothError; +use windows::Devices::Enumeration::DevicePairingResultStatus; + +use crate::bluetooth::common::{BluetoothError, PairingResult}; impl From for BluetoothError { fn from(err: windows::core::Error) -> Self { BluetoothError::System(err.to_string()) } } + +// https://learn.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicepairingresultstatus?view=winrt-22621 +impl From for PairingResult { + fn from(status: DevicePairingResultStatus) -> Self { + match status { + DevicePairingResultStatus::Paired => PairingResult::Success, + DevicePairingResultStatus::AlreadyPaired => { + PairingResult::AlreadyPaired + } + DevicePairingResultStatus::OperationAlreadyInProgress => { + PairingResult::AlreadyInProgress + } + DevicePairingResultStatus::NotReadyToPair => PairingResult::Failure( + String::from("the device object is not in a state where it can be paired"), + ), + DevicePairingResultStatus::NotPaired => PairingResult::Failure(String::from("the device object is not currently paired.")), + DevicePairingResultStatus::ConnectionRejected => PairingResult::Failure(String::from("the device object rejected the connection.")), + DevicePairingResultStatus::TooManyConnections => PairingResult::Failure(String::from("the device object indicated it cannot accept any more incoming connections.")), + DevicePairingResultStatus::HardwareFailure => PairingResult::Failure(String::from("the device object indicated there was a hardware failure.")), + DevicePairingResultStatus::AuthenticationTimeout => PairingResult::Failure(String::from("the authentication process timed out before it could complete.")), + DevicePairingResultStatus::AuthenticationNotAllowed => PairingResult::Failure(String::from("the authentication protocol is not supported, so the device is not paired.")), + DevicePairingResultStatus::AuthenticationFailure => PairingResult::Failure(String::from("authentication failed, so the device is not paired. Either the device object or the application rejected the authentication.")), + DevicePairingResultStatus::NoSupportedProfiles => PairingResult::Failure(String::from("there are no network profiles for this device object to use.")), + DevicePairingResultStatus::ProtectionLevelCouldNotBeMet => PairingResult::Failure(String::from("the minimum level of protection is not supported by the device object or the application.")), + DevicePairingResultStatus::AccessDenied => PairingResult::Failure(String::from("your application does not have the appropriate permissions level to pair the device object.")), + DevicePairingResultStatus::InvalidCeremonyData => PairingResult::Failure(String::from("the ceremony data was incorrect.")), + DevicePairingResultStatus::PairingCanceled => PairingResult::Failure(String::from("the pairing action was canceled before completion.")), + DevicePairingResultStatus::RequiredHandlerNotRegistered => PairingResult::Failure(String::from("either the event handler wasn't registered or a required DevicePairingKinds was not supported.",)), + DevicePairingResultStatus::RejectedByHandler => PairingResult::Failure(String::from("the application handler rejected the pairing.")), + DevicePairingResultStatus::RemoteDeviceHasAssociation => PairingResult::Failure(String::from("the remote device already has an association.")), + DevicePairingResultStatus::Failed | _ => PairingResult::Failure(String::from("an unknown failure occurred.")), + } + } +} diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index c0326ba6..7b796f5a 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -18,18 +18,48 @@ use futures::executor; mod bluetooth; -use bluetooth::{Adapter, Device}; +use bluetooth::{Adapter, Address, ClassicAddress, Device}; fn main() -> Result<(), Box> { let run = async { let mut adapter = bluetooth::default_adapter().await?; adapter.start_scan_devices()?; - while let Ok(device) = adapter.next_device().await { - println!("found {}", device.name()?) - } + while let Ok(ble_device) = adapter.next_device().await { + let name = ble_device.name()?; - unreachable!("Done scanning"); + if name.contains("LE_WF-1000XM3") { + println!("FOUND {} ", name); + + let addr: Address = ble_device.address(); + + // Dynamic dispatch is necessary here because `BleDevice` and + // `ClassicDevice` share the `Device` trait (and thus must have + // the same return type for `address()` method). This can be + // changed if `Device` trait should exclusively define + // cross-platform behavior. + let classic_addr = match addr { + Address::Ble(ble) => ClassicAddress::try_from(ble), + Address::Classic(_) => unreachable!( + "Address should come from BLE Device, therefore \ + shouldn't be Classic." + ), + }?; + + let classic_device = + bluetooth::new_classic_device(classic_addr).await?; + + match classic_device.pair().await { + Ok(_) => { + println!("Pairing success!"); + } + Err(err) => println!("Error {}", err), + } + break; + } + } + println!("Done scanning"); + Ok(()) }; executor::block_on(run) From 4be9872f6dfc9b2699be0cf2ea1eaf83568dca9b Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 14 Jul 2023 23:25:57 -0700 Subject: [PATCH 038/128] [fp-rs] Updated device scanning interface to facilitate adding more functionality to device constructor. --- fastpair/rust/src/bluetooth/common/adapter.rs | 8 +- fastpair/rust/src/bluetooth/common/address.rs | 11 +- fastpair/rust/src/bluetooth/common/error.rs | 4 + .../rust/src/bluetooth/unsupported/adapter.rs | 4 +- .../rust/src/bluetooth/windows/adapter.rs | 110 +++++++++--------- .../rust/src/bluetooth/windows/address.rs | 16 +-- fastpair/rust/src/main.rs | 2 +- 7 files changed, 79 insertions(+), 76 deletions(-) diff --git a/fastpair/rust/src/bluetooth/common/adapter.rs b/fastpair/rust/src/bluetooth/common/adapter.rs index 40a504ee..1b07e250 100644 --- a/fastpair/rust/src/bluetooth/common/adapter.rs +++ b/fastpair/rust/src/bluetooth/common/adapter.rs @@ -25,11 +25,11 @@ pub trait Adapter: Sized { /// Retrieve the system-default Bluetooth adapter. async fn default() -> Result; - /// Begin scanning for nearby devices. - fn start_scan_devices(&mut self) -> Result<(), BluetoothError>; + /// Begin scanning for nearby advertisements. + fn start_scan(&mut self) -> Result<(), BluetoothError>; - /// Stop scanning for nearby devices. - fn stop_scan_devices(&mut self) -> Result<(), BluetoothError>; + /// Stop scanning for nearby advertisements. + fn stop_scan(&mut self) -> Result<(), BluetoothError>; /// Poll next discovered device. async fn next_device(&mut self) -> Result; diff --git a/fastpair/rust/src/bluetooth/common/address.rs b/fastpair/rust/src/bluetooth/common/address.rs index dc54e067..9cbf2367 100644 --- a/fastpair/rust/src/bluetooth/common/address.rs +++ b/fastpair/rust/src/bluetooth/common/address.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::bluetooth::common::BluetoothError; + /// BLE Addresses can either be the peripheral's public MAC address, or various /// types of random addresses. #[derive(PartialEq, Eq, Clone, Copy, Debug)] @@ -68,15 +70,14 @@ impl From for ClassicAddress { } impl TryFrom for ClassicAddress { - // TODO proper error handling b/291931475 - type Error = anyhow::Error; + type Error = BluetoothError; fn try_from(addr: BleAddress) -> Result { match addr.kind { BleAddressKind::Public => Ok(ClassicAddress(addr.val)), - BleAddressKind::Random => Err(anyhow::anyhow!( - "Can't convert BLE Random address to Bluetooth Classic address." - )), + BleAddressKind::Random => Err(BluetoothError::BadTypeConversion(String::from( + "can't convert BLE Random address to Bluetooth Classic address." + ))), } } } diff --git a/fastpair/rust/src/bluetooth/common/error.rs b/fastpair/rust/src/bluetooth/common/error.rs index 5c9e49e9..8b20b36e 100644 --- a/fastpair/rust/src/bluetooth/common/error.rs +++ b/fastpair/rust/src/bluetooth/common/error.rs @@ -18,6 +18,10 @@ use thiserror::Error; #[non_exhaustive] #[derive(Error, Debug)] pub enum BluetoothError { + /// Reported when the user attempts a bad type conversion, e.g. converting + /// a BLE random address to a BT Classic address. + #[error("bad type conversion: {0}")] + BadTypeConversion(String), /// Reported when Bluetooth device pairing fails. #[error("pairing error: {0}")] PairingFailed(String), diff --git a/fastpair/rust/src/bluetooth/unsupported/adapter.rs b/fastpair/rust/src/bluetooth/unsupported/adapter.rs index 8baa3b81..2794b172 100644 --- a/fastpair/rust/src/bluetooth/unsupported/adapter.rs +++ b/fastpair/rust/src/bluetooth/unsupported/adapter.rs @@ -29,11 +29,11 @@ impl Adapter for BleAdapter { panic!("Unsupported target platform."); } - fn start_scan_devices(&mut self) -> Result<(), BluetoothError> { + fn start_scan(&mut self) -> Result<(), BluetoothError> { panic!("Unsupported target platform."); } - fn stop_scan_devices(&mut self) -> Result<(), BluetoothError> { + fn stop_scan(&mut self) -> Result<(), BluetoothError> { panic!("Unsupported target platform."); } diff --git a/fastpair/rust/src/bluetooth/windows/adapter.rs b/fastpair/rust/src/bluetooth/windows/adapter.rs index 57fb533d..3ffcfb33 100644 --- a/fastpair/rust/src/bluetooth/windows/adapter.rs +++ b/fastpair/rust/src/bluetooth/windows/adapter.rs @@ -12,12 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::pin::Pin; use std::sync::Arc; use async_trait::async_trait; -use futures::{stream::Stream, StreamExt}; -use tracing::{error, warn}; +use futures::{channel::mpsc::Receiver, StreamExt}; +use tracing::{error, info, warn}; use windows::{ Devices::Bluetooth::{ Advertisement::{ @@ -55,16 +54,23 @@ use windows::{ }; use super::BleDevice; -use crate::bluetooth::common::{Adapter, BleAddress, BleAddressKind, BluetoothError}; +use crate::bluetooth::common::{ + Adapter, BleAddress, BleAddressKind, BluetoothError, +}; + +/// Struct holding the necessary fields for listening to and handling incoming +/// BLE advertisements. +struct AdvListener { + /// Holds callback for sending received advertisement events to `receiver`. + watcher: BluetoothLEAdvertisementWatcher, + /// Can be polled to consume incoming advertisement events. + receiver: Receiver, +} /// Concrete type implementing `Adapter`, used for Windows BLE. pub struct BleAdapter { inner: BluetoothAdapter, - // NOTE: Using Boxed dyn here is silly because only one concrete type ever - // used. Change this to `impl Stream` once impl trait return types - // stabilized for existential types. - // b/289224233. - device_stream: Option + Send + Sync>>>, + listener: Option, } #[async_trait] @@ -87,11 +93,11 @@ impl Adapter for BleAdapter { Ok(BleAdapter { inner, - device_stream: None, + listener: None, }) } - fn start_scan_devices(&mut self) -> Result<(), BluetoothError> { + fn start_scan(&mut self) -> Result<(), BluetoothError> { let watcher = BluetoothLEAdvertisementWatcher::new()?; match watcher.SetScanningMode(BluetoothLEScanningMode::Active) { Ok(_) => (), @@ -149,7 +155,7 @@ impl Adapter for BleAdapter { >| { // Drop `sender`, closing the channel. let _sender = sender.take(); - println!("Watcher stopped receiving BLE advertisements."); + info!("Watcher stopped receiving BLE advertisements."); Ok(()) }, ); @@ -158,48 +164,14 @@ impl Adapter for BleAdapter { watcher.Stopped(&stopped_handler)?; watcher.Start()?; - // `receiver` is a `futures::channel::mpsc::Receiver`, which implements - // `futures::stream::Stream`. This is essentially an async Iterator. - // We apply a FilterMap to map from advertisement packet to a future - // returning `BleDevice` and filter out undesired connections. We need a - // pinned box to satisfy trait bounds for `Stream`. - self.device_stream = - Some(Box::pin(receiver.filter_map(move |event_args| { - // Move `watcher` into `FilterMap` closure. This ensures `watcher` - // is only dropped when the stream is closed. - let _watcher = &watcher; - - // Move `event_args` into async block. - async move { - match event_args.AdvertisementType().ok()? { - BluetoothLEAdvertisementType::NonConnectableUndirected => { - None - } - _ => { - let kind = event_args.BluetoothAddressType().ok()?; - let addr = event_args.BluetoothAddress().ok()?; - - let kind = BleAddressKind::try_from(kind).ok()?; - let addr = BleAddress::new(addr, kind); - - match BleDevice::new(addr).await { - Ok(device) => Some(device), - Err(err) => { - warn!("Error creating device: {:?}", err); - None - } - } - } - } - } - }))); + self.listener = Some(AdvListener { watcher, receiver }); Ok(()) } - fn stop_scan_devices(&mut self) -> Result<(), BluetoothError> { - if let Some(_) = &self.device_stream { - self.device_stream.take(); + fn stop_scan(&mut self) -> Result<(), BluetoothError> { + if let Some(listener) = self.listener.take() { + listener.watcher.Stop()?; Ok(()) } else { Err(BluetoothError::FailedPrecondition(String::from( @@ -209,13 +181,37 @@ impl Adapter for BleAdapter { } async fn next_device(&mut self) -> Result { - if let Some(stream) = &mut self.device_stream { - stream - .next() - .await - .ok_or(BluetoothError::Internal(String::from( - "device returned from Stream is None", - ))) + if let Some(listener) = &mut self.listener { + let stream = &mut listener.receiver; + // We don't want the end-user to receive empty devices, so this is a + // loop to catch and skip trivial errors from advertisements that + // can't be turned into devices. + loop { + let event_args = + stream.next().await.ok_or(BluetoothError::Internal( + String::from("Event returned from stream is None."), + ))?; + + match event_args.AdvertisementType()? { + BluetoothLEAdvertisementType::NonConnectableUndirected => { + () + } + _ => { + let kind = event_args.BluetoothAddressType()?; + let addr = event_args.BluetoothAddress()?; + + let kind = BleAddressKind::try_from(kind)?; + let addr = BleAddress::new(addr, kind); + + match BleDevice::new(addr).await { + Ok(device) => break Ok(device), + Err(err) => { + warn!("Error creating device: {:?}", err); + } + } + } + } + } } else { Err(BluetoothError::FailedPrecondition(String::from( "device scanning hasn't started, please call `start_scan()`", diff --git a/fastpair/rust/src/bluetooth/windows/address.rs b/fastpair/rust/src/bluetooth/windows/address.rs index 6f587237..d08ae4f3 100644 --- a/fastpair/rust/src/bluetooth/windows/address.rs +++ b/fastpair/rust/src/bluetooth/windows/address.rs @@ -16,21 +16,23 @@ //https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothaddresstype?view=winrt-22621 use windows::Devices::Bluetooth::BluetoothAddressType; -use crate::bluetooth::common::BleAddressKind; +use crate::bluetooth::common::{BleAddressKind, BluetoothError}; // Convenience for converting from Windows API to crate API. impl TryFrom for BleAddressKind { - type Error = anyhow::Error; + type Error = BluetoothError; fn try_from(kind: BluetoothAddressType) -> Result { match kind { BluetoothAddressType::Public => Ok(BleAddressKind::Public), BluetoothAddressType::Random => Ok(BleAddressKind::Random), - BluetoothAddressType::Unspecified => Err(anyhow::anyhow!( - "Attempting to construct `BleAddressKind` with device \ - advertising Unspecified address type." - )), - _ => Err(anyhow::anyhow!(format!( + BluetoothAddressType::Unspecified => { + Err(BluetoothError::BadTypeConversion(String::from( + "Attempting to construct `BleAddressKind` with device \ + advertising Unspecified address type.", + ))) + } + _ => Err(BluetoothError::BadTypeConversion(format!( "Attempting to construct `BleAddressKind` with device \ advertising invalid address type {}.", kind.0, diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index 7b796f5a..95c665c2 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -23,7 +23,7 @@ use bluetooth::{Adapter, Address, ClassicAddress, Device}; fn main() -> Result<(), Box> { let run = async { let mut adapter = bluetooth::default_adapter().await?; - adapter.start_scan_devices()?; + adapter.start_scan()?; while let Ok(ble_device) = adapter.next_device().await { let name = ble_device.name()?; From 81592cc4c5ee302c365c4b020eafa6436385eb07 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 14 Jul 2023 17:24:47 -0700 Subject: [PATCH 039/128] [fp-rs] Device now holds 16bit UUID service data, collected by Adapter. --- fastpair/rust/Cargo.toml | 2 + fastpair/rust/src/bluetooth/common/data.rs | 36 +++++++++++++ fastpair/rust/src/bluetooth/common/device.rs | 4 +- fastpair/rust/src/bluetooth/common/mod.rs | 2 + .../rust/src/bluetooth/windows/adapter.rs | 51 ++++++++++++++++--- fastpair/rust/src/bluetooth/windows/device.rs | 15 ++++-- fastpair/rust/src/main.rs | 4 +- 7 files changed, 101 insertions(+), 13 deletions(-) create mode 100644 fastpair/rust/src/bluetooth/common/data.rs diff --git a/fastpair/rust/Cargo.toml b/fastpair/rust/Cargo.toml index a8bc4428..02b48ebc 100644 --- a/fastpair/rust/Cargo.toml +++ b/fastpair/rust/Cargo.toml @@ -32,4 +32,6 @@ windows = { version = "0.48", features = [ "Devices_Enumeration", "Devices_Bluetooth_Advertisement", "Foundation", + "Foundation_Collections", + "Storage_Streams", ] } diff --git a/fastpair/rust/src/bluetooth/common/data.rs b/fastpair/rust/src/bluetooth/common/data.rs new file mode 100644 index 00000000..7ab7d57e --- /dev/null +++ b/fastpair/rust/src/bluetooth/common/data.rs @@ -0,0 +1,36 @@ +// 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. + +pub enum BleDataSection { + ServiceData16BitUUid = 0x16, +} + +pub struct ServiceData { + uuid: U, + data: Vec, +} + +impl ServiceData { + pub fn new(uuid: U, data: Vec) -> Self { + ServiceData { uuid, data } + } + + pub fn uuid(&self) -> U { + self.uuid + } + + pub fn data(&self) -> &Vec { + &self.data + } +} diff --git a/fastpair/rust/src/bluetooth/common/device.rs b/fastpair/rust/src/bluetooth/common/device.rs index a4c5756c..9c745df3 100644 --- a/fastpair/rust/src/bluetooth/common/device.rs +++ b/fastpair/rust/src/bluetooth/common/device.rs @@ -14,7 +14,7 @@ use async_trait::async_trait; -use super::{Address, BluetoothError, PairingResult}; +use super::{Address, BluetoothError, PairingResult, ServiceData}; /// Concrete types implementing this trait represent Bluetooth Peripheral devices. /// They provide methods for retrieving device info and running device actions, @@ -29,4 +29,6 @@ pub trait Device: Sized { /// Attempt pairing with the peripheral device. async fn pair(&self) -> Result; + + fn service_data(&self) -> &Vec>; } diff --git a/fastpair/rust/src/bluetooth/common/mod.rs b/fastpair/rust/src/bluetooth/common/mod.rs index f604ede6..5a03978a 100644 --- a/fastpair/rust/src/bluetooth/common/mod.rs +++ b/fastpair/rust/src/bluetooth/common/mod.rs @@ -15,10 +15,12 @@ /// Module for shared functionality between all Bluetooth platforms. mod adapter; mod address; +mod data; mod device; mod error; pub use adapter::*; pub use address::*; +pub use data::*; pub use device::*; pub use error::*; diff --git a/fastpair/rust/src/bluetooth/windows/adapter.rs b/fastpair/rust/src/bluetooth/windows/adapter.rs index 3ffcfb33..9d7c9ab8 100644 --- a/fastpair/rust/src/bluetooth/windows/adapter.rs +++ b/fastpair/rust/src/bluetooth/windows/adapter.rs @@ -51,11 +51,16 @@ use windows::{ // (e.g. Received and Stopped events in BluetoothLEAdvertisementWatcher). // https://learn.microsoft.com/en-us/uwp/api/windows.foundation.typedeventhandler-2?view=winrt-22621 Foundation::TypedEventHandler, + + // Struct for reading data from a Windows stream, like an IVectorView. + // https://learn.microsoft.com/en-us/uwp/api/windows.storage.streams.datareader?view=winrt-22621 + Storage::Streams::DataReader, }; use super::BleDevice; use crate::bluetooth::common::{ - Adapter, BleAddress, BleAddressKind, BluetoothError, + Adapter, BleAddress, BleAddressKind, BleDataSection, BluetoothError, + ServiceData, }; /// Struct holding the necessary fields for listening to and handling incoming @@ -73,6 +78,39 @@ pub struct BleAdapter { listener: Option, } +/// Parse the advertisement's service data. +/// Further Reading: +/// * `BleMedium::AdvertisementReceivedHandler` under +/// github.com/google/nearby/internal/platform/implementation/windows_ble/ble_medium.cc. +/// * Bluetooth Core Specification Supplement, Part A, Section 1.11. +/// * go/fast_pair_windows_data_parse. +#[inline] +fn get_service_data_16bit_uuid( + event_args: &BluetoothLEAdvertisementReceivedEventArgs, +) -> Result>, BluetoothError> { + let advertisement = event_args.Advertisement()?; + let mut service_data_vec = Vec::new(); + + // Note `service_data` is `!Send` and `!Sync`. This means processing must + // occur in a synchronous environment (namely, this function's scope). + // The compiler will complain if similar code is written between awaits + // in an async function. + for service_data in advertisement + .GetSectionsByType(BleDataSection::ServiceData16BitUUid as u8)? + { + let data_reader = DataReader::FromBuffer(&service_data.Data()?)?; + let uuid = data_reader.ReadUInt16()?; + + let unconsumed_buffer_len = + data_reader.UnconsumedBufferLength()? as usize; + let mut data = vec![0u8; unconsumed_buffer_len]; + data_reader.ReadBytes(&mut data)?; + + service_data_vec.push(ServiceData::new(uuid, data)); + } + Ok(service_data_vec) +} + #[async_trait] impl Adapter for BleAdapter { type Device = BleDevice; @@ -202,13 +240,12 @@ impl Adapter for BleAdapter { let kind = BleAddressKind::try_from(kind)?; let addr = BleAddress::new(addr, kind); + let service_data = + get_service_data_16bit_uuid(&event_args)?; - match BleDevice::new(addr).await { - Ok(device) => break Ok(device), - Err(err) => { - warn!("Error creating device: {:?}", err); - } - } + let device = BleDevice::new(addr, service_data).await?; + + break Ok(device); } } } diff --git a/fastpair/rust/src/bluetooth/windows/device.rs b/fastpair/rust/src/bluetooth/windows/device.rs index 7e9f9cf0..30f1fae7 100644 --- a/fastpair/rust/src/bluetooth/windows/device.rs +++ b/fastpair/rust/src/bluetooth/windows/device.rs @@ -49,12 +49,13 @@ use windows::{ Foundation::TypedEventHandler, }; -use crate::bluetooth::common::{Address, BleAddress, ClassicAddress, Device, BluetoothError, PairingResult}; +use crate::bluetooth::common::{Address, BleAddress, ClassicAddress, Device, ServiceData, BluetoothError, PairingResult}; /// Concrete type implementing `Device`, used for Windows BLE. pub struct BleDevice { inner: BluetoothLEDevice, addr: BleAddress, + service_data: Vec> } /// Concrete type implementing `Device`, used for Windows Bluetooth Classic. @@ -65,7 +66,7 @@ pub struct ClassicDevice { impl BleDevice { /// `BleDevice` constructor. - pub async fn new(addr: BleAddress) -> Result { + pub async fn new(addr: BleAddress, service_data: Vec>) -> Result { let kind = BluetoothAddressType::from(addr.get_kind()); let raw_addr = u64::from(addr); @@ -74,7 +75,7 @@ impl BleDevice { )? .await?; - Ok(BleDevice { inner, addr }) + Ok(BleDevice { inner, addr, service_data }) } } @@ -95,6 +96,10 @@ impl Device for BleDevice { // `ClassicDevice::pair` directly. unimplemented!("BLE Pairing is currently unsupported.") } + + fn service_data(&self) -> &Vec> { + &self.service_data + } } @@ -169,6 +174,10 @@ impl Device for ClassicDevice { } } } + + fn service_data(&self) -> &Vec> { + unimplemented!("Service data is currently unsupported for Classic devices.") + } } mod tests { diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index 95c665c2..56b290a3 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -36,8 +36,8 @@ fn main() -> Result<(), Box> { // Dynamic dispatch is necessary here because `BleDevice` and // `ClassicDevice` share the `Device` trait (and thus must have // the same return type for `address()` method). This can be - // changed if `Device` trait should exclusively define - // cross-platform behavior. + // changed later if `Device` trait should exclusively define + // shared cross-platform behavior. let classic_addr = match addr { Address::Ble(ble) => ClassicAddress::try_from(ble), Address::Classic(_) => unreachable!( From 7730a0f1db3ef3590eff1e617888fc7be2d393ca Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Mon, 17 Jul 2023 17:02:55 -0700 Subject: [PATCH 040/128] [fp-rs] Updating Fast Pair Seeker to filter out non-FP advertisements. --- fastpair/rust/src/main.rs | 45 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index 56b290a3..b7cb0937 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -28,34 +28,41 @@ fn main() -> Result<(), Box> { while let Ok(ble_device) = adapter.next_device().await { let name = ble_device.name()?; - if name.contains("LE_WF-1000XM3") { - println!("FOUND {} ", name); + for service_data in ble_device.service_data() { + let uuid = service_data.uuid(); - let addr: Address = ble_device.address(); + // This is a Fast Pair device. + if uuid == 0x2cfe { + if name.contains("LE_WF-1000XM3") { + println!("FOUND {} ", name); - // Dynamic dispatch is necessary here because `BleDevice` and - // `ClassicDevice` share the `Device` trait (and thus must have - // the same return type for `address()` method). This can be - // changed later if `Device` trait should exclusively define - // shared cross-platform behavior. - let classic_addr = match addr { - Address::Ble(ble) => ClassicAddress::try_from(ble), - Address::Classic(_) => unreachable!( + let addr: Address = ble_device.address(); + + // Dynamic dispatch is necessary here because `BleDevice` and + // `ClassicDevice` share the `Device` trait (and thus must have + // the same return type for `address()` method). This can be + // changed later if `Device` trait should exclusively define + // shared cross-platform behavior. + let classic_addr = match addr { + Address::Ble(ble) => ClassicAddress::try_from(ble), + Address::Classic(_) => panic!( "Address should come from BLE Device, therefore \ shouldn't be Classic." ), - }?; + }?; - let classic_device = - bluetooth::new_classic_device(classic_addr).await?; + let classic_device = + bluetooth::new_classic_device(classic_addr).await?; - match classic_device.pair().await { - Ok(_) => { - println!("Pairing success!"); + match classic_device.pair().await { + Ok(_) => { + println!("Pairing success!"); + } + Err(err) => println!("Error {}", err), + } + break; } - Err(err) => println!("Error {}", err), } - break; } } println!("Done scanning"); From ba945aa360dc90a643b49c911a3725273371891d Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sun, 16 Jul 2023 02:26:36 -0700 Subject: [PATCH 041/128] [fp-rs] Implemented device selection CLI. --- fastpair/rust/src/bluetooth/common/address.rs | 7 +- fastpair/rust/src/bluetooth/mod.rs | 4 +- fastpair/rust/src/main.rs | 115 +++++++++++++----- 3 files changed, 89 insertions(+), 37 deletions(-) diff --git a/fastpair/rust/src/bluetooth/common/address.rs b/fastpair/rust/src/bluetooth/common/address.rs index 9cbf2367..7071e8f8 100644 --- a/fastpair/rust/src/bluetooth/common/address.rs +++ b/fastpair/rust/src/bluetooth/common/address.rs @@ -16,24 +16,25 @@ use crate::bluetooth::common::BluetoothError; /// BLE Addresses can either be the peripheral's public MAC address, or various /// types of random addresses. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub enum BleAddressKind { Public, Random, } /// Struct representing a 48-bit BLE Address and its type. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub struct BleAddress { val: [u8; 6], kind: BleAddressKind, } /// Struct representing a 48-bit BT Classic address. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub struct ClassicAddress([u8; 6]); /// Enum for interfacing with Bluetooth Addresses. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub enum Address { Ble(BleAddress), Classic(ClassicAddress), diff --git a/fastpair/rust/src/bluetooth/mod.rs b/fastpair/rust/src/bluetooth/mod.rs index fb374de6..cd1a7247 100644 --- a/fastpair/rust/src/bluetooth/mod.rs +++ b/fastpair/rust/src/bluetooth/mod.rs @@ -23,14 +23,14 @@ pub use common::{Adapter, Address, BluetoothError, ClassicAddress, Device}; cfg_if::cfg_if! { if #[cfg(windows)] { mod windows; - use self::windows::{ClassicDevice, BleAdapter}; + pub use self::windows::{ClassicDevice, BleAdapter}; } else { mod unsupported; use unsupported::{ClassicDevice, BleAdapter}; } } -pub async fn default_adapter() -> Result { +pub async fn default_adapter() -> Result { BleAdapter::default().await } diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index b7cb0937..d9627525 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -12,56 +12,107 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::error::Error; +use std::{ + collections::HashSet, + error::Error, + io::{self, Write}, + sync::Arc, + thread, +}; -use futures::executor; +use futures::{ + executor::{self, block_on}, + lock::Mutex, +}; mod bluetooth; -use bluetooth::{Adapter, Address, ClassicAddress, Device}; +use bluetooth::{Adapter, Address, BleAdapter, ClassicAddress, Device}; + +async fn get_user_input( + device_vec: Arc::Device>>>, +) -> Result<(), Box> { + let mut buffer = String::new(); + loop { + io::stdout().flush()?; + buffer.clear(); + io::stdin().read_line(&mut buffer)?; + + let val = match buffer.trim().parse::() { + Ok(val) => val, + Err(_) => { + println!("Please enter a valid digit."); + continue; + } + }; + + let index_to_device = device_vec.lock().await; + match index_to_device.get(val) { + Some(device) => { + let addr: Address = device.address(); + + // Dynamic dispatch is necessary here because `BleDevice` and + // `ClassicDevice` share the `Device` trait (and thus must have + // the same return type for `address()` method). This can be + // changed later if `Device` trait should exclusively define + // shared cross-platform behavior. + let classic_addr = match addr { + Address::Ble(ble) => ClassicAddress::try_from(ble), + Address::Classic(_) => panic!( + "Address should come from BLE Device, therefore \ + shouldn't be Classic." + ), + }?; + + let classic_device = + bluetooth::new_classic_device(classic_addr).await?; + + match classic_device.pair().await { + Ok(_) => { + println!("Pairing success!"); + } + Err(err) => println!("Error {}", err), + } + break Ok(()); + } + None => println!("Please enter a valid digit."), + } + } +} fn main() -> Result<(), Box> { let run = async { let mut adapter = bluetooth::default_adapter().await?; adapter.start_scan()?; - while let Ok(ble_device) = adapter.next_device().await { - let name = ble_device.name()?; + let mut addr_set = HashSet::new(); + let device_vec = Arc::new(Mutex::new(Vec::new())); + { + // Process user input in a separate thread. + let device_vec = device_vec.clone(); + thread::spawn(|| block_on(get_user_input(device_vec)).unwrap()); + } + + let mut counter: u32 = 0; + + // Retrieve incoming device advertisements. + while let Ok(ble_device) = adapter.next_device().await { for service_data in ble_device.service_data() { let uuid = service_data.uuid(); // This is a Fast Pair device. if uuid == 0x2cfe { - if name.contains("LE_WF-1000XM3") { - println!("FOUND {} ", name); + let addr: Address = ble_device.address(); + let name = ble_device.name()?; - let addr: Address = ble_device.address(); - - // Dynamic dispatch is necessary here because `BleDevice` and - // `ClassicDevice` share the `Device` trait (and thus must have - // the same return type for `address()` method). This can be - // changed later if `Device` trait should exclusively define - // shared cross-platform behavior. - let classic_addr = match addr { - Address::Ble(ble) => ClassicAddress::try_from(ble), - Address::Classic(_) => panic!( - "Address should come from BLE Device, therefore \ - shouldn't be Classic." - ), - }?; - - let classic_device = - bluetooth::new_classic_device(classic_addr).await?; - - match classic_device.pair().await { - Ok(_) => { - println!("Pairing success!"); - } - Err(err) => println!("Error {}", err), - } - break; + if addr_set.insert(addr) { + // New FP device discovered. + println!("{}: {}", counter, name); + device_vec.lock().await.push(ble_device); + counter += 1; } + break; } } } From f42fdb418641965b555ef28ec1fc6caaa986f2f6 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Thu, 20 Jul 2023 11:00:05 -0700 Subject: [PATCH 042/128] [fp-rs] Temporarily removing bluetooth lib abstraction layer, applications now talk directly to platform code. Rust's type system is causing issues with notating associated types. For now, concrete types will be used, but this will be fixed when the API is updated with splitting Device into ClassicDevice and BleDevice. --- fastpair/rust/src/bluetooth/common/address.rs | 7 ------ fastpair/rust/src/bluetooth/common/device.rs | 6 +++-- fastpair/rust/src/bluetooth/mod.rs | 17 ++++--------- fastpair/rust/src/bluetooth/windows/device.rs | 14 +++++++---- fastpair/rust/src/main.rs | 24 +++++-------------- 5 files changed, 24 insertions(+), 44 deletions(-) diff --git a/fastpair/rust/src/bluetooth/common/address.rs b/fastpair/rust/src/bluetooth/common/address.rs index 7071e8f8..485ff731 100644 --- a/fastpair/rust/src/bluetooth/common/address.rs +++ b/fastpair/rust/src/bluetooth/common/address.rs @@ -33,13 +33,6 @@ pub struct BleAddress { #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub struct ClassicAddress([u8; 6]); -/// Enum for interfacing with Bluetooth Addresses. -#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] -pub enum Address { - Ble(BleAddress), - Classic(ClassicAddress), -} - impl BleAddress { /// `BleAddress` constructor. pub fn new(addr: u64, kind: BleAddressKind) -> Self { diff --git a/fastpair/rust/src/bluetooth/common/device.rs b/fastpair/rust/src/bluetooth/common/device.rs index 9c745df3..30e277ae 100644 --- a/fastpair/rust/src/bluetooth/common/device.rs +++ b/fastpair/rust/src/bluetooth/common/device.rs @@ -14,18 +14,20 @@ use async_trait::async_trait; -use super::{Address, BluetoothError, PairingResult, ServiceData}; +use super::{BluetoothError, PairingResult, ServiceData}; /// Concrete types implementing this trait represent Bluetooth Peripheral devices. /// They provide methods for retrieving device info and running device actions, /// such as pairing. #[async_trait] pub trait Device: Sized { + type Address; + /// Retrieve the name advertised by this device. fn name(&self) -> Result; /// Retrieve this device's Bluetooth address information. - fn address(&self) -> Address; + fn address(&self) -> Self::Address; /// Attempt pairing with the peripheral device. async fn pair(&self) -> Result; diff --git a/fastpair/rust/src/bluetooth/mod.rs b/fastpair/rust/src/bluetooth/mod.rs index cd1a7247..c996bcd6 100644 --- a/fastpair/rust/src/bluetooth/mod.rs +++ b/fastpair/rust/src/bluetooth/mod.rs @@ -18,24 +18,17 @@ pub mod common; -pub use common::{Adapter, Address, BluetoothError, ClassicAddress, Device}; +pub use common::{Adapter, BluetoothError, ClassicAddress, Device}; cfg_if::cfg_if! { if #[cfg(windows)] { mod windows; - pub use self::windows::{ClassicDevice, BleAdapter}; + use self::windows as platform; } else { mod unsupported; - use unsupported::{ClassicDevice, BleAdapter}; + use unsupported as platform; } } -pub async fn default_adapter() -> Result { - BleAdapter::default().await -} - -pub async fn new_classic_device( - addr: ClassicAddress, -) -> Result { - ClassicDevice::new(addr).await -} +pub type BleAdapter = platform::BleAdapter; +pub type ClassicDevice = platform::ClassicDevice; diff --git a/fastpair/rust/src/bluetooth/windows/device.rs b/fastpair/rust/src/bluetooth/windows/device.rs index 30f1fae7..a1551211 100644 --- a/fastpair/rust/src/bluetooth/windows/device.rs +++ b/fastpair/rust/src/bluetooth/windows/device.rs @@ -49,7 +49,7 @@ use windows::{ Foundation::TypedEventHandler, }; -use crate::bluetooth::common::{Address, BleAddress, ClassicAddress, Device, ServiceData, BluetoothError, PairingResult}; +use crate::bluetooth::{common::{BleAddress, ClassicAddress, Device, ServiceData, BluetoothError, PairingResult}, BleAdapter}; /// Concrete type implementing `Device`, used for Windows BLE. pub struct BleDevice { @@ -81,12 +81,14 @@ impl BleDevice { #[async_trait] impl Device for BleDevice { + type Address = BleAddress; + fn name(&self) -> Result { Ok(self.inner.Name()?.to_string()) } - fn address(&self) -> Address { - Address::Ble(self.addr) + fn address(&self) -> Self::Address { + self.addr } async fn pair(&self) -> Result { @@ -119,12 +121,14 @@ impl ClassicDevice { #[async_trait] impl Device for ClassicDevice { + type Address = ClassicAddress; + fn name(&self) -> Result { Ok(self.inner.Name()?.to_string_lossy()) } - fn address(&self) -> Address { - Address::Classic(self.addr) + fn address(&self) -> Self::Address { + self.addr } async fn pair(&self) -> Result { diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index d9627525..ab41a698 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -27,7 +27,7 @@ use futures::{ mod bluetooth; -use bluetooth::{Adapter, Address, BleAdapter, ClassicAddress, Device}; +use bluetooth::{Adapter, BleAdapter, ClassicAddress, Device}; async fn get_user_input( device_vec: Arc::Device>>>, @@ -49,23 +49,11 @@ async fn get_user_input( let index_to_device = device_vec.lock().await; match index_to_device.get(val) { Some(device) => { - let addr: Address = device.address(); - - // Dynamic dispatch is necessary here because `BleDevice` and - // `ClassicDevice` share the `Device` trait (and thus must have - // the same return type for `address()` method). This can be - // changed later if `Device` trait should exclusively define - // shared cross-platform behavior. - let classic_addr = match addr { - Address::Ble(ble) => ClassicAddress::try_from(ble), - Address::Classic(_) => panic!( - "Address should come from BLE Device, therefore \ - shouldn't be Classic." - ), - }?; + let addr = device.address(); + let classic_addr = ClassicAddress::try_from(addr)?; let classic_device = - bluetooth::new_classic_device(classic_addr).await?; + bluetooth::ClassicDevice::new(classic_addr).await?; match classic_device.pair().await { Ok(_) => { @@ -82,7 +70,7 @@ async fn get_user_input( fn main() -> Result<(), Box> { let run = async { - let mut adapter = bluetooth::default_adapter().await?; + let mut adapter = bluetooth::BleAdapter::default().await?; adapter.start_scan()?; let mut addr_set = HashSet::new(); @@ -103,7 +91,7 @@ fn main() -> Result<(), Box> { // This is a Fast Pair device. if uuid == 0x2cfe { - let addr: Address = ble_device.address(); + let addr = ble_device.address(); let name = ble_device.name()?; if addr_set.insert(addr) { From 25a3257880314ed589dd533b10289a99eeaed963 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Mon, 24 Jul 2023 10:41:46 -0700 Subject: [PATCH 043/128] [fp-rs] Moving service data and advertisement information into separate BleAdvertisement struct. --- fastpair/rust/src/bluetooth/common/adapter.rs | 9 +- .../src/bluetooth/common/advertisement.rs | 89 +++++++++++++++ fastpair/rust/src/bluetooth/common/data.rs | 36 ------ fastpair/rust/src/bluetooth/common/device.rs | 4 +- fastpair/rust/src/bluetooth/common/mod.rs | 4 +- fastpair/rust/src/bluetooth/mod.rs | 6 +- .../rust/src/bluetooth/windows/adapter.rs | 64 ++--------- .../src/bluetooth/windows/advertisement.rs | 108 ++++++++++++++++++ fastpair/rust/src/bluetooth/windows/device.rs | 15 +-- fastpair/rust/src/bluetooth/windows/mod.rs | 2 + fastpair/rust/src/main.rs | 17 ++- 11 files changed, 238 insertions(+), 116 deletions(-) create mode 100644 fastpair/rust/src/bluetooth/common/advertisement.rs delete mode 100644 fastpair/rust/src/bluetooth/common/data.rs create mode 100644 fastpair/rust/src/bluetooth/windows/advertisement.rs diff --git a/fastpair/rust/src/bluetooth/common/adapter.rs b/fastpair/rust/src/bluetooth/common/adapter.rs index 1b07e250..f6dda1b9 100644 --- a/fastpair/rust/src/bluetooth/common/adapter.rs +++ b/fastpair/rust/src/bluetooth/common/adapter.rs @@ -14,14 +14,12 @@ use async_trait::async_trait; -use super::{BluetoothError, Device}; +use super::{BleAdvertisement, BleDataTypeId, BluetoothError}; /// Concrete types implementing this trait are Bluetooth Central devices. /// They provide methods for retrieving nearby connections and device info. #[async_trait] pub trait Adapter: Sized { - type Device: Device; - /// Retrieve the system-default Bluetooth adapter. async fn default() -> Result; @@ -32,5 +30,8 @@ pub trait Adapter: Sized { fn stop_scan(&mut self) -> Result<(), BluetoothError>; /// Poll next discovered device. - async fn next_device(&mut self) -> Result; + async fn next_advertisement( + &mut self, + data_selector: Option<&Vec>, + ) -> Result; } diff --git a/fastpair/rust/src/bluetooth/common/advertisement.rs b/fastpair/rust/src/bluetooth/common/advertisement.rs new file mode 100644 index 00000000..8260936e --- /dev/null +++ b/fastpair/rust/src/bluetooth/common/advertisement.rs @@ -0,0 +1,89 @@ +// 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. + +use super::{BleAddress, BluetoothError}; + +/// Holds data related to an incoming BLE Advertisement. This includes +/// information about the advertisement (e.g. address of sender) as well as +/// data sections extracted from the advertisement. Platform-specific methods +/// should be written to load in data sections from incoming advertisements. +pub struct BleAdvertisement { + address: BleAddress, + service_data_16bit_uuid: Option>>, +} + +impl BleAdvertisement { + /// Construct a new `BleAdvertisement` instance. + pub(crate) fn new(address: BleAddress) -> Self { + BleAdvertisement { + address, + service_data_16bit_uuid: None, + } + } + + /// Retrieve the `BleAddress` that emitted this advertisement. + pub fn address(&self) -> BleAddress { + self.address + } + + /// Setter for `ServiceData` field with 16bit UUID. + pub(crate) fn set_service_data_16bit_uuid( + &mut self, + data_sections: Vec>, + ) { + self.service_data_16bit_uuid = Some(data_sections); + } + + /// Getter for `ServiceData` field with 16bit UUID. + pub fn service_data_16bit_uuid( + &self, + ) -> Result<&Vec>, BluetoothError> { + match &self.service_data_16bit_uuid { + Some(service_data) => Ok(&service_data), + None => Err(BluetoothError::FailedPrecondition(String::from( + "No service data has been loaded into this advertisement.", + ))), + } + } +} + +/// Enum denoting the assigned number of Bluetooth common data types. Used for +/// fetching specific data sections from a Bluetooth advertisement. +/// Bluetooth Assigned Numbers, Section 2.3 +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum BleDataTypeId { + ServiceData16BitUuid = 0x16, +} + +/// Struct representing the Bluetooth Service Data common data type. `U` should +/// be one of the valid uuid sizes, specified in: +/// Bluetooth Supplement to the Core Specification, Part A, Section 1.11. +pub struct ServiceData { + uuid: U, + data: Vec, +} + +impl ServiceData { + pub fn new(uuid: U, data: Vec) -> Self { + ServiceData { uuid, data } + } + + pub fn uuid(&self) -> U { + self.uuid + } + + pub fn data(&self) -> &Vec { + &self.data + } +} diff --git a/fastpair/rust/src/bluetooth/common/data.rs b/fastpair/rust/src/bluetooth/common/data.rs deleted file mode 100644 index 7ab7d57e..00000000 --- a/fastpair/rust/src/bluetooth/common/data.rs +++ /dev/null @@ -1,36 +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. - -pub enum BleDataSection { - ServiceData16BitUUid = 0x16, -} - -pub struct ServiceData { - uuid: U, - data: Vec, -} - -impl ServiceData { - pub fn new(uuid: U, data: Vec) -> Self { - ServiceData { uuid, data } - } - - pub fn uuid(&self) -> U { - self.uuid - } - - pub fn data(&self) -> &Vec { - &self.data - } -} diff --git a/fastpair/rust/src/bluetooth/common/device.rs b/fastpair/rust/src/bluetooth/common/device.rs index 30e277ae..1f861155 100644 --- a/fastpair/rust/src/bluetooth/common/device.rs +++ b/fastpair/rust/src/bluetooth/common/device.rs @@ -14,7 +14,7 @@ use async_trait::async_trait; -use super::{BluetoothError, PairingResult, ServiceData}; +use super::{BluetoothError, PairingResult}; /// Concrete types implementing this trait represent Bluetooth Peripheral devices. /// They provide methods for retrieving device info and running device actions, @@ -31,6 +31,4 @@ pub trait Device: Sized { /// Attempt pairing with the peripheral device. async fn pair(&self) -> Result; - - fn service_data(&self) -> &Vec>; } diff --git a/fastpair/rust/src/bluetooth/common/mod.rs b/fastpair/rust/src/bluetooth/common/mod.rs index 5a03978a..13b05114 100644 --- a/fastpair/rust/src/bluetooth/common/mod.rs +++ b/fastpair/rust/src/bluetooth/common/mod.rs @@ -15,12 +15,12 @@ /// Module for shared functionality between all Bluetooth platforms. mod adapter; mod address; -mod data; +mod advertisement; mod device; mod error; pub use adapter::*; pub use address::*; -pub use data::*; +pub use advertisement::*; pub use device::*; pub use error::*; diff --git a/fastpair/rust/src/bluetooth/mod.rs b/fastpair/rust/src/bluetooth/mod.rs index c996bcd6..979a27c7 100644 --- a/fastpair/rust/src/bluetooth/mod.rs +++ b/fastpair/rust/src/bluetooth/mod.rs @@ -18,7 +18,10 @@ pub mod common; -pub use common::{Adapter, BluetoothError, ClassicAddress, Device}; +pub use common::{ + Adapter, BleAdvertisement, BleDataTypeId, BluetoothError, ClassicAddress, + Device, +}; cfg_if::cfg_if! { if #[cfg(windows)] { @@ -31,4 +34,5 @@ cfg_if::cfg_if! { } pub type BleAdapter = platform::BleAdapter; +pub type BleDevice = platform::BleDevice; pub type ClassicDevice = platform::ClassicDevice; diff --git a/fastpair/rust/src/bluetooth/windows/adapter.rs b/fastpair/rust/src/bluetooth/windows/adapter.rs index 9d7c9ab8..2eb3ded2 100644 --- a/fastpair/rust/src/bluetooth/windows/adapter.rs +++ b/fastpair/rust/src/bluetooth/windows/adapter.rs @@ -51,16 +51,10 @@ use windows::{ // (e.g. Received and Stopped events in BluetoothLEAdvertisementWatcher). // https://learn.microsoft.com/en-us/uwp/api/windows.foundation.typedeventhandler-2?view=winrt-22621 Foundation::TypedEventHandler, - - // Struct for reading data from a Windows stream, like an IVectorView. - // https://learn.microsoft.com/en-us/uwp/api/windows.storage.streams.datareader?view=winrt-22621 - Storage::Streams::DataReader, }; -use super::BleDevice; use crate::bluetooth::common::{ - Adapter, BleAddress, BleAddressKind, BleDataSection, BluetoothError, - ServiceData, + Adapter, BleAdvertisement, BleDataTypeId, BluetoothError, }; /// Struct holding the necessary fields for listening to and handling incoming @@ -78,43 +72,8 @@ pub struct BleAdapter { listener: Option, } -/// Parse the advertisement's service data. -/// Further Reading: -/// * `BleMedium::AdvertisementReceivedHandler` under -/// github.com/google/nearby/internal/platform/implementation/windows_ble/ble_medium.cc. -/// * Bluetooth Core Specification Supplement, Part A, Section 1.11. -/// * go/fast_pair_windows_data_parse. -#[inline] -fn get_service_data_16bit_uuid( - event_args: &BluetoothLEAdvertisementReceivedEventArgs, -) -> Result>, BluetoothError> { - let advertisement = event_args.Advertisement()?; - let mut service_data_vec = Vec::new(); - - // Note `service_data` is `!Send` and `!Sync`. This means processing must - // occur in a synchronous environment (namely, this function's scope). - // The compiler will complain if similar code is written between awaits - // in an async function. - for service_data in advertisement - .GetSectionsByType(BleDataSection::ServiceData16BitUUid as u8)? - { - let data_reader = DataReader::FromBuffer(&service_data.Data()?)?; - let uuid = data_reader.ReadUInt16()?; - - let unconsumed_buffer_len = - data_reader.UnconsumedBufferLength()? as usize; - let mut data = vec![0u8; unconsumed_buffer_len]; - data_reader.ReadBytes(&mut data)?; - - service_data_vec.push(ServiceData::new(uuid, data)); - } - Ok(service_data_vec) -} - #[async_trait] impl Adapter for BleAdapter { - type Device = BleDevice; - async fn default() -> Result { let inner = BluetoothAdapter::GetDefaultAsync()?.await?; @@ -218,7 +177,10 @@ impl Adapter for BleAdapter { } } - async fn next_device(&mut self) -> Result { + async fn next_advertisement( + &mut self, + datatype_selector: Option<&Vec>, + ) -> Result { if let Some(listener) = &mut self.listener { let stream = &mut listener.receiver; // We don't want the end-user to receive empty devices, so this is a @@ -235,17 +197,15 @@ impl Adapter for BleAdapter { () } _ => { - let kind = event_args.BluetoothAddressType()?; - let addr = event_args.BluetoothAddress()?; + let mut advertisement = + BleAdvertisement::try_from(&event_args)?; - let kind = BleAddressKind::try_from(kind)?; - let addr = BleAddress::new(addr, kind); - let service_data = - get_service_data_16bit_uuid(&event_args)?; + if let Some(datatype_selector) = datatype_selector { + advertisement + .load_data(&event_args, datatype_selector)?; + } - let device = BleDevice::new(addr, service_data).await?; - - break Ok(device); + break Ok(advertisement); } } } diff --git a/fastpair/rust/src/bluetooth/windows/advertisement.rs b/fastpair/rust/src/bluetooth/windows/advertisement.rs new file mode 100644 index 00000000..526e2d5a --- /dev/null +++ b/fastpair/rust/src/bluetooth/windows/advertisement.rs @@ -0,0 +1,108 @@ +// 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. + +use windows::{ + // Struct that receives Bluetooth Low Energy (LE) advertisements. + // https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.advertisement.bluetoothleadvertisementwatcher?view=winrt-22621 + Devices::Bluetooth::Advertisement::{ + BluetoothLEAdvertisementDataSection, + BluetoothLEAdvertisementReceivedEventArgs, + }, + + // Struct representing an immutable view into a vector. + // https://learn.microsoft.com/en-us/uwp/api/windows.foundation.collections.ivectorview-1?view=winrt-22621 + Foundation::Collections::IVectorView, + + // Struct for reading data from a Windows stream, like an IVectorView. + // https://learn.microsoft.com/en-us/uwp/api/windows.storage.streams.datareader?view=winrt-22621 + Storage::Streams::DataReader, +}; + +use crate::bluetooth::common::{ + BleAddress, BleAddressKind, BleAdvertisement, BleDataTypeId, + BluetoothError, ServiceData, +}; + +impl TryFrom<&BluetoothLEAdvertisementReceivedEventArgs> for BleAdvertisement { + type Error = BluetoothError; + + fn try_from( + adv: &BluetoothLEAdvertisementReceivedEventArgs, + ) -> Result { + let addr = adv.BluetoothAddress()?; + let kind = BleAddressKind::try_from(adv.BluetoothAddressType()?)?; + + let addr = BleAddress::new(addr, kind); + + Ok(BleAdvertisement::new(addr)) + } +} + +impl BleAdvertisement { + /// Load data of selected data types into self by parsing the raw Windows + /// advertisement. + /// See: Supplement to the Bluetooth Core Specification Part A, Section 1. + pub(crate) fn load_data( + &mut self, + adv: &BluetoothLEAdvertisementReceivedEventArgs, + datatype_ids: &[BleDataTypeId], + ) -> Result<(), BluetoothError> { + let adv = adv.Advertisement()?; + + for datatype_id in datatype_ids { + // Note `raw_data_sections` is `!Send` and `!Sync`. This means + // processing must occur in a synchronous environment. The compiler + // will complain if parsing is done in an async function. + let raw_data_sections = + adv.GetSectionsByType((*datatype_id) as u8)?; + match datatype_id { + BleDataTypeId::ServiceData16BitUuid => { + let service_data = + parse_service_data_16bit_uuid(raw_data_sections)?; + self.set_service_data_16bit_uuid(service_data) + } + }; + } + + Ok(()) + } +} + +/// Parse the advertisement's service data. +/// Further Reading: +/// * `BleMedium::AdvertisementReceivedHandler` under +/// github.com/google/nearby/internal/platform/implementation/windows_ble/ble_medium.cc. +/// * Bluetooth Supplement to the Core Specification, Part A, Section 1.11. +/// * go/fast_pair_windows_data_parse. +#[inline] +fn parse_service_data_16bit_uuid( + raw_data_sections: IVectorView, +) -> Result>, BluetoothError> { + let mut data_vec = Vec::new(); + + for raw_data in raw_data_sections { + let data_reader = DataReader::FromBuffer(&raw_data.Data()?)?; + let uuid = data_reader.ReadUInt16()?; + + let unconsumed_buffer_len = + data_reader.UnconsumedBufferLength()? as usize; + + let mut data = vec![0u8; unconsumed_buffer_len]; + data_reader.ReadBytes(&mut data)?; + + data_vec.push(ServiceData::new(uuid, data)); + } + + Ok(data_vec) +} diff --git a/fastpair/rust/src/bluetooth/windows/device.rs b/fastpair/rust/src/bluetooth/windows/device.rs index a1551211..6c3cc742 100644 --- a/fastpair/rust/src/bluetooth/windows/device.rs +++ b/fastpair/rust/src/bluetooth/windows/device.rs @@ -49,13 +49,12 @@ use windows::{ Foundation::TypedEventHandler, }; -use crate::bluetooth::{common::{BleAddress, ClassicAddress, Device, ServiceData, BluetoothError, PairingResult}, BleAdapter}; +use crate::bluetooth::common::{BleAddress, ClassicAddress, Device, BluetoothError, PairingResult}; /// Concrete type implementing `Device`, used for Windows BLE. pub struct BleDevice { inner: BluetoothLEDevice, addr: BleAddress, - service_data: Vec> } /// Concrete type implementing `Device`, used for Windows Bluetooth Classic. @@ -66,7 +65,7 @@ pub struct ClassicDevice { impl BleDevice { /// `BleDevice` constructor. - pub async fn new(addr: BleAddress, service_data: Vec>) -> Result { + pub async fn new(addr: BleAddress) -> Result { let kind = BluetoothAddressType::from(addr.get_kind()); let raw_addr = u64::from(addr); @@ -75,7 +74,7 @@ impl BleDevice { )? .await?; - Ok(BleDevice { inner, addr, service_data }) + Ok(BleDevice { inner, addr }) } } @@ -98,10 +97,6 @@ impl Device for BleDevice { // `ClassicDevice::pair` directly. unimplemented!("BLE Pairing is currently unsupported.") } - - fn service_data(&self) -> &Vec> { - &self.service_data - } } @@ -178,10 +173,6 @@ impl Device for ClassicDevice { } } } - - fn service_data(&self) -> &Vec> { - unimplemented!("Service data is currently unsupported for Classic devices.") - } } mod tests { diff --git a/fastpair/rust/src/bluetooth/windows/mod.rs b/fastpair/rust/src/bluetooth/windows/mod.rs index f23035c5..8003fe00 100644 --- a/fastpair/rust/src/bluetooth/windows/mod.rs +++ b/fastpair/rust/src/bluetooth/windows/mod.rs @@ -15,9 +15,11 @@ /// Bluetooth LE module for Windows devices. mod adapter; mod address; +mod advertisement; mod device; mod error; pub use adapter::*; pub use address::*; +pub use advertisement::*; pub use device::*; diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index ab41a698..8c45fa9a 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -27,10 +27,12 @@ use futures::{ mod bluetooth; -use bluetooth::{Adapter, BleAdapter, ClassicAddress, Device}; +use bluetooth::{Adapter, BleDevice, ClassicAddress, Device}; + +use crate::bluetooth::BleDataTypeId; async fn get_user_input( - device_vec: Arc::Device>>>, + device_vec: Arc>>, ) -> Result<(), Box> { let mut buffer = String::new(); loop { @@ -83,15 +85,18 @@ fn main() -> Result<(), Box> { } let mut counter: u32 = 0; - + let datatype_selector = vec![BleDataTypeId::ServiceData16BitUuid]; // Retrieve incoming device advertisements. - while let Ok(ble_device) = adapter.next_device().await { - for service_data in ble_device.service_data() { + while let Ok(advertisement) = + adapter.next_advertisement(Some(&datatype_selector)).await + { + for service_data in advertisement.service_data_16bit_uuid()? { let uuid = service_data.uuid(); // This is a Fast Pair device. if uuid == 0x2cfe { - let addr = ble_device.address(); + let addr = advertisement.address(); + let ble_device = BleDevice::new(addr).await?; let name = ble_device.name()?; if addr_set.insert(addr) { From e73a9bb9a8796bc155342615bf7c6acb3f0739a8 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 28 Jul 2023 14:58:15 -0700 Subject: [PATCH 044/128] [fp-rs] Updated API for accessing platform-specific structs. Previously, `Device` trait was implemented by both Classic and BLE Devices. This forced usage of associated types for choosing the type of address employed, bringing about limitations of Rust's type system. By ensuring cross-platform traits are only used for shared cross-platform behavior, there are no longer type annotation issues in the application layer. Changes: * Split the `Device` trait into `ClassicDevice` and `BleDevice`. * Migrated these to an `api/` module to avoid name clashes and clearly differentiate common structs (under `common/) from common traits (under `api/)`. * Removed shared `Address` enum in order to get rid of device's address associated type. * Application layer can now only talk to impl traits rather than the concrete platform-specific type. This ensures compile-time behavior guarantees. Constructors for cross-platform impls provided under the `Platform` unit struct. * Updated `unsupported` module for unsupported platforms. Previously, dummy inherent impls would've been necessary for calling the `new()` method. This is now part of the `ClassicDevice` and `BleDevice` API, so cross-platform behavior is guaranteed to work. --- .../src/bluetooth/{common => api}/adapter.rs | 6 +- fastpair/rust/src/bluetooth/api/device.rs | 56 +++++++++++++++++++ fastpair/rust/src/bluetooth/api/mod.rs | 5 ++ fastpair/rust/src/bluetooth/common/device.rs | 34 ----------- fastpair/rust/src/bluetooth/common/mod.rs | 4 -- fastpair/rust/src/bluetooth/mod.rs | 28 ++++++++-- .../rust/src/bluetooth/unsupported/adapter.rs | 15 +++-- .../rust/src/bluetooth/unsupported/device.rs | 45 +++++++++++++-- .../rust/src/bluetooth/windows/adapter.rs | 9 +-- fastpair/rust/src/bluetooth/windows/device.rs | 37 +++--------- fastpair/rust/src/main.rs | 15 ++--- 11 files changed, 160 insertions(+), 94 deletions(-) rename fastpair/rust/src/bluetooth/{common => api}/adapter.rs (91%) create mode 100644 fastpair/rust/src/bluetooth/api/device.rs create mode 100644 fastpair/rust/src/bluetooth/api/mod.rs delete mode 100644 fastpair/rust/src/bluetooth/common/device.rs diff --git a/fastpair/rust/src/bluetooth/common/adapter.rs b/fastpair/rust/src/bluetooth/api/adapter.rs similarity index 91% rename from fastpair/rust/src/bluetooth/common/adapter.rs rename to fastpair/rust/src/bluetooth/api/adapter.rs index f6dda1b9..39ab9310 100644 --- a/fastpair/rust/src/bluetooth/common/adapter.rs +++ b/fastpair/rust/src/bluetooth/api/adapter.rs @@ -14,12 +14,14 @@ use async_trait::async_trait; -use super::{BleAdvertisement, BleDataTypeId, BluetoothError}; +use crate::bluetooth::common::{ + BleAdvertisement, BleDataTypeId, BluetoothError, +}; /// Concrete types implementing this trait are Bluetooth Central devices. /// They provide methods for retrieving nearby connections and device info. #[async_trait] -pub trait Adapter: Sized { +pub trait BleAdapter: Sized { /// Retrieve the system-default Bluetooth adapter. async fn default() -> Result; diff --git a/fastpair/rust/src/bluetooth/api/device.rs b/fastpair/rust/src/bluetooth/api/device.rs new file mode 100644 index 00000000..97cba1d8 --- /dev/null +++ b/fastpair/rust/src/bluetooth/api/device.rs @@ -0,0 +1,56 @@ +// 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. + +use async_trait::async_trait; + +use crate::bluetooth::common::{ + BleAddress, BluetoothError, ClassicAddress, PairingResult, +}; + +/// Concrete types implementing this trait represent BLE Peripheral devices. +/// They provide methods for retrieving device info and running device actions, +/// such as pairing. +#[async_trait] +pub trait BleDevice: Sized { + /// Create a new `BleDevice` instance from a `BleAddress`, typically + /// enabled through locally cached data retrieved from a Bluetooth adapter's + /// scanning functionality. + async fn new(addr: BleAddress) -> Result; + + /// Retrieve the name advertised by this device. + fn name(&self) -> Result; + + /// Retrieve this device's Bluetooth address information. + fn address(&self) -> BleAddress; +} + +/// Concrete types implementing this trait represent BT Classic Peripheral +/// devices. They provide methods for retrieving device info and running device +/// actions, such as pairing. +#[async_trait] +pub trait ClassicDevice: Sized { + /// Create a new `ClassicDevice` instance from a `ClassicAddress`, typically + /// enabled through locally cached data retrieved from a Bluetooth adapter's + /// scanning functionality. + async fn new(addr: ClassicAddress) -> Result; + + /// Retrieve the name advertised by this device. + fn name(&self) -> Result; + + /// Retrieve this device's Bluetooth address information. + fn address(&self) -> ClassicAddress; + + /// Attempt pairing with the peripheral device. + async fn pair(&self) -> Result; +} diff --git a/fastpair/rust/src/bluetooth/api/mod.rs b/fastpair/rust/src/bluetooth/api/mod.rs new file mode 100644 index 00000000..0e410749 --- /dev/null +++ b/fastpair/rust/src/bluetooth/api/mod.rs @@ -0,0 +1,5 @@ +mod adapter; +mod device; + +pub use adapter::*; +pub use device::*; diff --git a/fastpair/rust/src/bluetooth/common/device.rs b/fastpair/rust/src/bluetooth/common/device.rs deleted file mode 100644 index 1f861155..00000000 --- a/fastpair/rust/src/bluetooth/common/device.rs +++ /dev/null @@ -1,34 +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. - -use async_trait::async_trait; - -use super::{BluetoothError, PairingResult}; - -/// Concrete types implementing this trait represent Bluetooth Peripheral devices. -/// They provide methods for retrieving device info and running device actions, -/// such as pairing. -#[async_trait] -pub trait Device: Sized { - type Address; - - /// Retrieve the name advertised by this device. - fn name(&self) -> Result; - - /// Retrieve this device's Bluetooth address information. - fn address(&self) -> Self::Address; - - /// Attempt pairing with the peripheral device. - async fn pair(&self) -> Result; -} diff --git a/fastpair/rust/src/bluetooth/common/mod.rs b/fastpair/rust/src/bluetooth/common/mod.rs index 13b05114..1ddd9b30 100644 --- a/fastpair/rust/src/bluetooth/common/mod.rs +++ b/fastpair/rust/src/bluetooth/common/mod.rs @@ -13,14 +13,10 @@ // limitations under the License. /// Module for shared functionality between all Bluetooth platforms. -mod adapter; mod address; mod advertisement; -mod device; mod error; -pub use adapter::*; pub use address::*; pub use advertisement::*; -pub use device::*; pub use error::*; diff --git a/fastpair/rust/src/bluetooth/mod.rs b/fastpair/rust/src/bluetooth/mod.rs index 979a27c7..0acdb250 100644 --- a/fastpair/rust/src/bluetooth/mod.rs +++ b/fastpair/rust/src/bluetooth/mod.rs @@ -16,11 +16,12 @@ // instead of using anyhow. // b/290070686 +pub mod api; pub mod common; +pub use api::{BleAdapter, BleDevice, ClassicDevice}; pub use common::{ - Adapter, BleAdvertisement, BleDataTypeId, BluetoothError, ClassicAddress, - Device, + BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError, ClassicAddress, }; cfg_if::cfg_if! { @@ -33,6 +34,23 @@ cfg_if::cfg_if! { } } -pub type BleAdapter = platform::BleAdapter; -pub type BleDevice = platform::BleDevice; -pub type ClassicDevice = platform::ClassicDevice; +pub struct Platform; + +impl Platform { + pub async fn default_adapter( + ) -> Result { + platform::BleAdapter::default().await + } + + pub async fn new_ble_device( + addr: BleAddress, + ) -> Result { + platform::BleDevice::new(addr).await + } + + pub async fn new_classic_device( + addr: ClassicAddress, + ) -> Result { + platform::ClassicDevice::new(addr).await + } +} diff --git a/fastpair/rust/src/bluetooth/unsupported/adapter.rs b/fastpair/rust/src/bluetooth/unsupported/adapter.rs index 2794b172..de883e3c 100644 --- a/fastpair/rust/src/bluetooth/unsupported/adapter.rs +++ b/fastpair/rust/src/bluetooth/unsupported/adapter.rs @@ -15,16 +15,16 @@ use async_trait::async_trait; use super::BleDevice; -use crate::bluetooth::common::{Adapter, BluetoothError}; +use crate::bluetooth::{ + api, common::BluetoothError, BleAdvertisement, BleDataTypeId, +}; /// Concrete type implementing `Adapter`, used for unsupported devices. /// Every method should panic. pub struct BleAdapter; #[async_trait] -impl Adapter for BleAdapter { - type Device = BleDevice; - +impl api::BleAdapter for BleAdapter { async fn default() -> Result { panic!("Unsupported target platform."); } @@ -37,8 +37,11 @@ impl Adapter for BleAdapter { panic!("Unsupported target platform."); } - async fn next_device(&mut self) -> Result { - panic!("Unsupported target platform."); + async fn next_advertisement( + &mut self, + datatype_selector: Option<&Vec>, + ) -> Result { + panic!("Unsupported target platform"); } } diff --git a/fastpair/rust/src/bluetooth/unsupported/device.rs b/fastpair/rust/src/bluetooth/unsupported/device.rs index d18d1e34..0ac68775 100644 --- a/fastpair/rust/src/bluetooth/unsupported/device.rs +++ b/fastpair/rust/src/bluetooth/unsupported/device.rs @@ -12,15 +12,52 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bluetooth::common::{BluetoothError, Device}; +use async_trait::async_trait; -/// Concrete type implementing `Device`, used for unsupported devices. +use crate::bluetooth::{ + api, + common::{BleAddress, BluetoothError, ClassicAddress, PairingResult}, +}; + +/// Concrete type implementing `api::BleDevice` for unsupported platforms. /// Every method should panic. pub struct BleDevice; -impl Device for BleDevice { +#[async_trait] +impl api::BleDevice for BleDevice { + async fn new(addr: BleAddress) -> Result { + panic!("Unsupported target platform."); + } + fn name(&self) -> Result { - panic!("Unsupported target platform.") + panic!("Unsupported target platform."); + } + + fn address(&self) -> BleAddress { + panic!("Unsupported target platform."); + } +} + +/// Concrete type implementing `api::ClassicDevice` for unsupported platforms. +/// Every method should panic. +pub struct ClassicDevice; + +#[async_trait] +impl api::ClassicDevice for ClassicDevice { + async fn new(addr: ClassicAddress) -> Result { + panic!("Unsupported target platform."); + } + + fn name(&self) -> Result { + panic!("Unsupported target platform."); + } + + fn address(&self) -> ClassicAddress { + panic!("Unsupported target platform."); + } + + async fn pair(&self) -> Result { + panic!("Unsupported target platform."); } } diff --git a/fastpair/rust/src/bluetooth/windows/adapter.rs b/fastpair/rust/src/bluetooth/windows/adapter.rs index 2eb3ded2..cf4519f6 100644 --- a/fastpair/rust/src/bluetooth/windows/adapter.rs +++ b/fastpair/rust/src/bluetooth/windows/adapter.rs @@ -53,8 +53,9 @@ use windows::{ Foundation::TypedEventHandler, }; -use crate::bluetooth::common::{ - Adapter, BleAdvertisement, BleDataTypeId, BluetoothError, +use crate::bluetooth::{ + api, + common::{BleAdvertisement, BleDataTypeId, BluetoothError}, }; /// Struct holding the necessary fields for listening to and handling incoming @@ -66,14 +67,14 @@ struct AdvListener { receiver: Receiver, } -/// Concrete type implementing `Adapter`, used for Windows BLE. +/// Concrete type implementing `api::BleAdapter`, used for Windows BLE. pub struct BleAdapter { inner: BluetoothAdapter, listener: Option, } #[async_trait] -impl Adapter for BleAdapter { +impl api::BleAdapter for BleAdapter { async fn default() -> Result { let inner = BluetoothAdapter::GetDefaultAsync()?.await?; diff --git a/fastpair/rust/src/bluetooth/windows/device.rs b/fastpair/rust/src/bluetooth/windows/device.rs index 6c3cc742..cf0e293f 100644 --- a/fastpair/rust/src/bluetooth/windows/device.rs +++ b/fastpair/rust/src/bluetooth/windows/device.rs @@ -49,7 +49,7 @@ use windows::{ Foundation::TypedEventHandler, }; -use crate::bluetooth::common::{BleAddress, ClassicAddress, Device, BluetoothError, PairingResult}; +use crate::bluetooth::{api, common::{BleAddress, ClassicAddress, BluetoothError, PairingResult}}; /// Concrete type implementing `Device`, used for Windows BLE. pub struct BleDevice { @@ -63,9 +63,9 @@ pub struct ClassicDevice { addr: ClassicAddress, } -impl BleDevice { - /// `BleDevice` constructor. - pub async fn new(addr: BleAddress) -> Result { +#[async_trait] +impl api::BleDevice for BleDevice { + async fn new(addr: BleAddress) -> Result { let kind = BluetoothAddressType::from(addr.get_kind()); let raw_addr = u64::from(addr); @@ -76,33 +76,19 @@ impl BleDevice { Ok(BleDevice { inner, addr }) } -} - -#[async_trait] -impl Device for BleDevice { - type Address = BleAddress; fn name(&self) -> Result { Ok(self.inner.Name()?.to_string()) } - fn address(&self) -> Self::Address { + fn address(&self) -> BleAddress { self.addr } - - async fn pair(&self) -> Result { - // BLE Audio isn't supported on Windows natively, so devices can pair - // but don't playback. Might possibly work with UWP. Since the Classic - // and BLE APIs are very similar, it might be possible to copy-paste - // `ClassicDevice::pair` directly. - unimplemented!("BLE Pairing is currently unsupported.") - } } - -impl ClassicDevice { - /// `ClassicDevice` constructor. - pub async fn new(addr: ClassicAddress) -> Result { +#[async_trait] +impl api::ClassicDevice for ClassicDevice { + async fn new(addr: ClassicAddress) -> Result { let raw_addr = u64::from(addr); let inner = BluetoothDevice::FromBluetoothAddressAsync( @@ -112,17 +98,12 @@ impl ClassicDevice { Ok(ClassicDevice { inner, addr }) } -} - -#[async_trait] -impl Device for ClassicDevice { - type Address = ClassicAddress; fn name(&self) -> Result { Ok(self.inner.Name()?.to_string_lossy()) } - fn address(&self) -> Self::Address { + fn address(&self) -> ClassicAddress { self.addr } diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index 8c45fa9a..19d77c00 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -27,12 +27,13 @@ use futures::{ mod bluetooth; -use bluetooth::{Adapter, BleDevice, ClassicAddress, Device}; - -use crate::bluetooth::BleDataTypeId; +use crate::bluetooth::{ + BleAdapter, BleDataTypeId, BleDevice, ClassicAddress, ClassicDevice, + Platform, +}; async fn get_user_input( - device_vec: Arc>>, + device_vec: Arc>>, ) -> Result<(), Box> { let mut buffer = String::new(); loop { @@ -55,7 +56,7 @@ async fn get_user_input( let classic_addr = ClassicAddress::try_from(addr)?; let classic_device = - bluetooth::ClassicDevice::new(classic_addr).await?; + Platform::new_classic_device(classic_addr).await?; match classic_device.pair().await { Ok(_) => { @@ -72,7 +73,7 @@ async fn get_user_input( fn main() -> Result<(), Box> { let run = async { - let mut adapter = bluetooth::BleAdapter::default().await?; + let mut adapter = Platform::default_adapter().await?; adapter.start_scan()?; let mut addr_set = HashSet::new(); @@ -96,7 +97,7 @@ fn main() -> Result<(), Box> { // This is a Fast Pair device. if uuid == 0x2cfe { let addr = advertisement.address(); - let ble_device = BleDevice::new(addr).await?; + let ble_device = Platform::new_ble_device(addr).await?; let name = ble_device.name()?; if addr_set.insert(addr) { From 4a100435f79bbc3c6d78127d2a29ff4473269bf2 Mon Sep 17 00:00:00 2001 From: hai007 Date: Sun, 30 Jul 2023 20:30:42 -0700 Subject: [PATCH 045/128] Apply result code to BLE L2CAP mediums PiperOrigin-RevId: 552347968 --- proto/connections_enums.proto | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/proto/connections_enums.proto b/proto/connections_enums.proto index 8af849d9..c5d25ae5 100644 --- a/proto/connections_enums.proto +++ b/proto/connections_enums.proto @@ -822,6 +822,10 @@ enum OperationResultDetail { CONNECTIVITY_WIFI_AWARE_DISCOVERED_PEER_NULL = 3552; // Failed to write chunk to endpoint channel CONNECTIVITY_GENERIC_PAYLOAD_SENT_ERROR = 3553; + // L2CAP server socket creation failure (SecurityException) + CONNECTIVITY_L2CAP_SERVER_SOCKET_CREATION_SECURITY_EXCEPTION_FAILURE = 3554; + // BT server socket creation failure (SecurityException) + CONNECTIVITY_BT_SERVER_SOCKET_CREATION_SECURITY_EXCEPTION_FAILURE = 3555; // Section of CATEGORY_NEARBY_ERROR, from 4500 // NO BLE MAC address associated to the GATT advertisement From 0d58ead61c1a3b255928f5d56b02c1580218f76b Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Mon, 31 Jul 2023 13:21:23 -0700 Subject: [PATCH 046/128] Internal change PiperOrigin-RevId: 552574409 --- internal/platform/BUILD | 2 ++ proto/BUILD | 1 + 2 files changed, 3 insertions(+) diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 36dbd344..31aa5857 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Placeholder: load py_test + licenses(["notice"]) cc_library( diff --git a/proto/BUILD b/proto/BUILD index 1d40471a..7abb0cd9 100644 --- a/proto/BUILD +++ b/proto/BUILD @@ -14,6 +14,7 @@ # Proto for Nearby products +# Placeholder: load py_proto_library load("@rules_cc//cc:defs.bzl", "cc_proto_library") licenses(["notice"]) From 9d2cfc19c954bb6164f2a490c6027f0e5570703f Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Mon, 31 Jul 2023 17:18:52 -0700 Subject: [PATCH 047/128] Add Objective-C++ GATT server wrapper PiperOrigin-RevId: 552636264 --- Package.swift | 1 + internal/platform/implementation/apple/BUILD | 4 + .../implementation/apple/ble_gatt_server.h | 78 +++++++++++++ .../implementation/apple/ble_gatt_server.mm | 104 ++++++++++++++++++ .../implementation/apple/ble_peripheral.h | 1 + 5 files changed, 188 insertions(+) create mode 100644 internal/platform/implementation/apple/ble_gatt_server.h create mode 100644 internal/platform/implementation/apple/ble_gatt_server.mm diff --git a/Package.swift b/Package.swift index e4c984bd..5f66797b 100644 --- a/Package.swift +++ b/Package.swift @@ -572,6 +572,7 @@ let package = Package( "internal/platform/medium_environment.cc", // Temporarily ignore BLEv2 source files. // TODO(b/293283024): Stop ignoring these files when BLEv2 migration is complete. + "internal/platform/implementation/apple/ble_gatt_server.mm", "internal/platform/implementation/apple/ble_peripheral.mm", "internal/platform/implementation/apple/ble_server_socket.mm", "internal/platform/implementation/apple/ble_socket.mm", diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 98af2cba..0bd4ef8d 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -107,6 +107,7 @@ objc_library( objc_library( name = "ble_v2", srcs = [ + "ble_gatt_server.mm", "ble_peripheral.mm", "ble_server_socket.mm", "ble_socket.mm", @@ -115,6 +116,7 @@ objc_library( "utils.mm", ], hdrs = [ + "ble_gatt_server.h", "ble_peripheral.h", "ble_server_socket.h", "ble_socket.h", @@ -126,10 +128,12 @@ objc_library( aspect_hints = ["//tools/build_defs/swift:no_module"], deps = [ "//internal/platform:base", + "//internal/platform:uuid", "//internal/platform/implementation:comm", "//internal/platform/implementation/apple/Mediums", "//third_party/apple_frameworks:CoreBluetooth", "//third_party/apple_frameworks:Foundation", + "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/strings", diff --git a/internal/platform/implementation/apple/ble_gatt_server.h b/internal/platform/implementation/apple/ble_gatt_server.h new file mode 100644 index 00000000..e42d4998 --- /dev/null +++ b/internal/platform/implementation/apple/ble_gatt_server.h @@ -0,0 +1,78 @@ +// 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. + +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data + +#import + +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/uuid.h" + +#import "internal/platform/implementation/apple/ble_peripheral.h" + +@class GNCBLEGATTServer; + +namespace nearby { +namespace apple { + +class GattServer : public api::ble_v2::GattServer { + public: + explicit GattServer(GNCBLEGATTServer *gatt_server_); + ~GattServer() override = default; + + // Returns an empty BlePeripheral object. + // + // Use of this method should be avoided and its only purpose seems to be a check that the GATT + // server is valid. + api::ble_v2::BlePeripheral &GetBlePeripheral() override; + + // Creates a characteristic and adds it to the GATT server under the given characteristic and + // service UUIDs. + // + // Characteristics of the same service UUID will be put under one service rather than many + // services with the same UUID. + // + // Returns no value upon error. + std::optional CreateCharacteristic( + const Uuid &service_uuid, const Uuid &characteristic_uuid, + api::ble_v2::GattCharacteristic::Permission permission, + api::ble_v2::GattCharacteristic::Property property) override; + + // Updates a local characteristic with the provided value. + // + // Returns whether or not the update was successful. + bool UpdateCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, + const nearby::ByteArray &value) override; + + // Send a notification or indication that a local characteristic has been updated. + // + // Returns an absl::Status indicating success or what went wrong. + absl::Status NotifyCharacteristicChanged(const api::ble_v2::GattCharacteristic &characteristic, + bool confirm, const ByteArray &new_value) override; + + // Stops a GATT server. + void Stop() override; + + private: + GNCBLEGATTServer *gatt_server_; + BlePeripheral peripheral_; +}; + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_gatt_server.mm b/internal/platform/implementation/apple/ble_gatt_server.mm new file mode 100644 index 00000000..57682c73 --- /dev/null +++ b/internal/platform/implementation/apple/ble_gatt_server.mm @@ -0,0 +1,104 @@ +// 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/ble_gatt_server.h" + +#import +#import + +#include "internal/platform/implementation/ble_v2.h" + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h" +#import "internal/platform/implementation/apple/ble_utils.h" +#import "internal/platform/implementation/apple/utils.h" +#import "GoogleToolboxForMac/GTMLogger.h" + +namespace nearby { +namespace apple { + +GattServer::GattServer(GNCBLEGATTServer *gatt_server) : gatt_server_(gatt_server) {} + +std::optional GattServer::CreateCharacteristic( + const Uuid &service_uuid, const Uuid &characteristic_uuid, + api::ble_v2::GattCharacteristic::Permission permission, + api::ble_v2::GattCharacteristic::Property property) { + CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); + CBUUID *characteristicUUID = CBUUID128FromCPP(characteristic_uuid); + CBAttributePermissions permissions = CBAttributePermissionsFromCPP(permission); + CBCharacteristicProperties properties = CBCharacteristicPropertiesFromCPP(property); + + NSCondition *condition = [[NSCondition alloc] init]; + [condition lock]; + __block GNCBLEGATTCharacteristic *blockCharacteristic = nil; + [gatt_server_ createCharacteristicWithServiceID:serviceUUID + characteristicUUID:characteristicUUID + permissions:permissions + properties:properties + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + [condition lock]; + if (error != nil) { + GTMLoggerError(@"Error creating characteristic: %@", error); + } + blockCharacteristic = characteristic; + [condition signal]; + [condition unlock]; + }]; + [condition wait]; + [condition unlock]; + if (blockCharacteristic == nil) { + return std::nullopt; + } + return CPPGATTCharacteristicFromObjC(blockCharacteristic); +} + +bool GattServer::UpdateCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, + const nearby::ByteArray &value) { + NSCondition *condition = [[NSCondition alloc] init]; + [condition lock]; + __block NSError *blockError = nil; + [gatt_server_ updateCharacteristic:ObjCGATTCharacteristicFromCPP(characteristic) + value:NSDataFromByteArray(value) + completionHandler:^(NSError *error) { + [condition lock]; + if (error != nil) { + GTMLoggerError(@"Error updating characteristic: %@", error); + } + blockError = error; + [condition signal]; + [condition unlock]; + }]; + [condition wait]; + [condition unlock]; + return blockError == nil; +} + +// TODO(b/290385712): Implement. +absl::Status GattServer::NotifyCharacteristicChanged( + const api::ble_v2::GattCharacteristic &characteristic, bool confirm, + const ByteArray &new_value) { + return absl::UnimplementedError(""); +} + +void GattServer::Stop() { + [gatt_server_ stop]; +} + +// TODO(b/290385712): Implement. +api::ble_v2::BlePeripheral &GattServer::GetBlePeripheral() { + return peripheral_; +} + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_peripheral.h b/internal/platform/implementation/apple/ble_peripheral.h index b31b918e..c17c5a4d 100644 --- a/internal/platform/implementation/apple/ble_peripheral.h +++ b/internal/platform/implementation/apple/ble_peripheral.h @@ -33,6 +33,7 @@ namespace apple { // identify a peripheral and connect to its GATT server. class BlePeripheral : public api::ble_v2::BlePeripheral { public: + BlePeripheral() = default; explicit BlePeripheral(CBPeripheral *peripheral); ~BlePeripheral() override = default; From 868d3ce046217d3deb4f21d09d9c040b86804179 Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Mon, 31 Jul 2023 19:12:12 -0700 Subject: [PATCH 048/128] Resolved the issue to monitor lock/unlock in platform SDK PiperOrigin-RevId: 552655345 --- .../platform/implementation/windows/BUILD | 3 + .../implementation/windows/device_info.cc | 159 ++---------- .../implementation/windows/device_info.h | 23 +- .../windows/device_info_test.cc | 55 ---- .../implementation/windows/session_manager.cc | 238 ++++++++++++++++++ .../implementation/windows/session_manager.h | 57 +++++ 6 files changed, 323 insertions(+), 212 deletions(-) create mode 100644 internal/platform/implementation/windows/session_manager.cc create mode 100644 internal/platform/implementation/windows/session_manager.h diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index 4ba8c0de..9e4ac2f9 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -11,6 +11,7 @@ # 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( @@ -95,6 +96,7 @@ cc_library( "mutex.h", "scheduled_executor.h", "server_sync.h", + "session_manager.h", "submittable_executor.h", "thread_pool.h", "webrtc.h", @@ -166,6 +168,7 @@ cc_library( "preferences_repository.cc", "preferences_repository.h", "scheduled_executor.cc", + "session_manager.cc", "submittable_executor.cc", "system_clock.cc", "thread_pool.cc", diff --git a/internal/platform/implementation/windows/device_info.cc b/internal/platform/implementation/windows/device_info.cc index c9ef9adb..eb3220c5 100644 --- a/internal/platform/implementation/windows/device_info.cc +++ b/internal/platform/implementation/windows/device_info.cc @@ -23,11 +23,14 @@ #include #include #include +#include #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" #include "internal/base/bluetooth_address.h" #include "internal/platform/implementation/device_info.h" +#include "internal/platform/implementation/windows/session_manager.h" #include "internal/platform/logging.h" #include "winrt/Windows.Foundation.Collections.h" #include "winrt/Windows.Foundation.h" @@ -49,89 +52,10 @@ using IVectorView = winrt::Windows::Foundation::Collections::IVectorView; template using IAsyncOperation = winrt::Windows::Foundation::IAsyncOperation; -constexpr char window_class_name[] = "NearbySharingDLL_MessageWindowClass"; -constexpr char window_name[] = "NearbySharingDLL_MessageWindow"; constexpr char logs_relative_path[] = "Google\\Nearby\\Sharing\\Logs"; constexpr char crash_dumps_relative_path[] = "Google\\Nearby\\Sharing\\CrashDumps"; -namespace { -// This WindowProc method must be static for the successful initialization of -// WNDCLASS -// window_class.lpfnWndProc = (WNDPROC) &DeviceInfo::WindowProc; -// where a WNDPROC typed function pointer is expected -// typedef LRESULT (CALLBACK* WNDPROC)(HWND,UINT,WPARAM,LPARAM) -// the calling convention used here CALLBACK is a macro defined as -// #define CALLBACK __stdcall -// -// If WindProc is not static and defined as a member function, it uses the -// __thiscall calling convention instead -// https://docs.microsoft.com/en-us/cpp/cpp/thiscall?view=msvc-170 -// https://isocpp.org/wiki/faq/pointers-to-members -// https://en.cppreference.com/w/cpp/language/pointer -// -// This is problematic because the function pointer now looks like this -// typedef LRESULT (CALLBACK* DeviceInfo_WNDPROC)(DeviceInfo* -// this,HWND,UINT,WPARAM,LPARAM) -// which causes casting errors -LRESULT CALLBACK WindowProc(HWND window_handle, UINT message, WPARAM wparam, - LPARAM lparam) { - DeviceInfo* self = reinterpret_cast( - GetWindowLongPtr(window_handle, GWLP_USERDATA)); - CREATESTRUCT* create_struct = reinterpret_cast(lparam); - LONG_PTR result = 0L; - switch (message) { - case WM_CREATE: - self = reinterpret_cast(create_struct->lpCreateParams); - self->message_window_handle_ = window_handle; - - // Store pointer to the self to the window's user data. - SetLastError(0); - result = SetWindowLongPtr(window_handle, GWLP_USERDATA, - reinterpret_cast(self)); - if (result == 0 && GetLastError() != 0) { - NEARBY_LOGS(ERROR) - << __func__ - << ": Error connecting message window to Nearby Sharing DLL."; - } - break; - case WM_WTSSESSION_CHANGE: - if (self) { - switch (wparam) { - case WTS_SESSION_LOCK: - for (auto& listener : self->screen_locked_listeners_) { - listener.second(api::DeviceInfo::ScreenStatus:: - kLocked); // Trigger registered callbacks - } - break; - case WTS_SESSION_UNLOCK: - for (auto& listener : self->screen_locked_listeners_) { - listener.second(api::DeviceInfo::ScreenStatus:: - kUnlocked); // Trigger registered callbacks - } - break; - } - } - break; - case WM_DESTROY: - SetLastError(0); - result = SetWindowLongPtr(window_handle, GWLP_USERDATA, NULL); - if (result == 0 && GetLastError() != 0) { - NEARBY_LOGS(ERROR) - << __func__ - << ": Error disconnecting message window to Nearby Sharing DLL."; - } - break; - } - - return DefWindowProc(window_handle, message, wparam, lparam); -} -} // namespace - -DeviceInfo::~DeviceInfo() { - UnregisterClass(MAKEINTATOM(registered_class_), instance_); -} - std::optional DeviceInfo::GetOsDeviceName() const { DWORD size = 0; @@ -404,78 +328,29 @@ std::optional DeviceInfo::GetCrashDumpPath() const { } bool DeviceInfo::IsScreenLocked() const { - DWORD session_id = WTSGetActiveConsoleSessionId(); - WTS_INFO_CLASS wts_info_class = WTSSessionInfoEx; - LPTSTR session_info_buffer = nullptr; - DWORD session_info_buffer_size_bytes = 0; - - WTSINFOEXW* wts_info = nullptr; - LONG session_state = WTS_SESSIONSTATE_UNKNOWN; - - if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session_id, - wts_info_class, &session_info_buffer, - &session_info_buffer_size_bytes)) { - if (session_info_buffer_size_bytes > 0) { - wts_info = (WTSINFOEXW*)session_info_buffer; - if (wts_info->Level == 1) { - session_state = wts_info->Data.WTSInfoExLevel1.SessionFlags; - } - } - WTSFreeMemory(session_info_buffer); - session_info_buffer = nullptr; - } - - return (session_state == WTS_SESSIONSTATE_LOCK); + absl::MutexLock lock(&mutex_); + return session_manager_.IsScreenLocked(); } void DeviceInfo::RegisterScreenLockedListener( absl::string_view listener_name, std::function callback) { - if (message_window_handle_ == nullptr) { - instance_ = (HINSTANCE)GetModuleHandle(NULL); - - WNDCLASS window_class; - window_class.style = 0; - window_class.lpfnWndProc = (WNDPROC)&WindowProc; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = instance_; - window_class.hIcon = nullptr; - window_class.hCursor = nullptr; - window_class.hbrBackground = nullptr; - window_class.lpszMenuName = nullptr; - window_class.lpszClassName = window_class_name; - - registered_class_ = RegisterClass(&window_class); - - message_window_handle_ = CreateWindow( - MAKEINTATOM(registered_class_), // class atom from RegisterClass - window_name, // window name - 0, // window style - 0, // initial x position of window - 0, // initial y position of window - 0, // width - 0, // height - HWND_MESSAGE, // handle to the parent of window - // (message-only window in this case) - nullptr, // handle to a menu - instance_, // handle to the instance of the module - // associated to the window - this); // pointer to be passed to the window for additional data - - if (!message_window_handle_) { - NEARBY_LOGS(ERROR) - << __func__ - << ": Failed to create message window for Nearby Sharing DLL."; - } - } - - screen_locked_listeners_.emplace(listener_name, callback); + absl::MutexLock lock(&mutex_); + session_manager_.RegisterSessionListener( + listener_name, + [callback = std::move(callback)](SessionManager::SessionState state) { + if (state == SessionManager::SessionState::kLock) { + callback(api::DeviceInfo::ScreenStatus::kLocked); + } else if (state == SessionManager::SessionState::kUnlock) { + callback(api::DeviceInfo::ScreenStatus::kUnlocked); + } + }); } void DeviceInfo::UnregisterScreenLockedListener( absl::string_view listener_name) { - screen_locked_listeners_.erase(listener_name); + absl::MutexLock lock(&mutex_); + session_manager_.UnregisterSessionListener(listener_name); } } // namespace windows diff --git a/internal/platform/implementation/windows/device_info.h b/internal/platform/implementation/windows/device_info.h index b8ea3995..9d112d30 100644 --- a/internal/platform/implementation/windows/device_info.h +++ b/internal/platform/implementation/windows/device_info.h @@ -1,4 +1,4 @@ -// Copyright 2021 Google LLC +// 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. @@ -15,25 +15,21 @@ #ifndef PLATFORM_IMPL_WINDOWS_DEVICE_INFO_H_ #define PLATFORM_IMPL_WINDOWS_DEVICE_INFO_H_ -#include -#include - -#include #include #include -#include -#include "absl/container/flat_hash_map.h" +#include "absl/base/thread_annotations.h" #include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" #include "internal/platform/implementation/device_info.h" -#include "winrt/Windows.Foundation.h" +#include "internal/platform/implementation/windows/session_manager.h" namespace nearby { namespace windows { class DeviceInfo : public api::DeviceInfo { public: - ~DeviceInfo() override; + ~DeviceInfo() override = default; std::optional GetOsDeviceName() const override; api::DeviceInfo::DeviceType GetDeviceType() const override; @@ -55,13 +51,10 @@ class DeviceInfo : public api::DeviceInfo { absl::string_view listener_name, std::function callback) override; void UnregisterScreenLockedListener(absl::string_view listener_name) override; - absl::flat_hash_map> - screen_locked_listeners_; - HINSTANCE instance_ = nullptr; - ATOM registered_class_ = NULL; - HWND message_window_handle_ = nullptr; + private: + mutable absl::Mutex mutex_; + SessionManager session_manager_ ABSL_GUARDED_BY(mutex_); }; } // namespace windows diff --git a/internal/platform/implementation/windows/device_info_test.cc b/internal/platform/implementation/windows/device_info_test.cc index 89c076b3..4cdbf306 100644 --- a/internal/platform/implementation/windows/device_info_test.cc +++ b/internal/platform/implementation/windows/device_info_test.cc @@ -77,61 +77,6 @@ TEST(DeviceInfo, DISABLED_IsScreenLocked) { EXPECT_FALSE(DeviceInfo().IsScreenLocked()); } -TEST(DeviceInfo, DISABLED_RegisterScreenLockedListener) { - std::function listener_1 = - [](api::DeviceInfo::ScreenStatus) {}; - std::function listener_2 = - [](api::DeviceInfo::ScreenStatus) {}; - - DeviceInfo device_info; - EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); - - device_info.RegisterScreenLockedListener("listener_1", listener_1); - EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1); - - device_info.RegisterScreenLockedListener("listener_2", listener_2); - EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2); -} - -TEST(DeviceInfo, DISABLED_UnregisterScreenLockedListener) { - std::function listener_1 = - [](api::DeviceInfo::ScreenStatus) {}; - std::function listener_2 = - [](api::DeviceInfo::ScreenStatus) {}; - - DeviceInfo device_info; - EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); - - device_info.RegisterScreenLockedListener("listener_1", listener_1); - device_info.RegisterScreenLockedListener("listener_2", listener_2); - EXPECT_EQ(device_info.screen_locked_listeners_.size(), 2); - - device_info.UnregisterScreenLockedListener("listener_1"); - EXPECT_EQ(device_info.screen_locked_listeners_.size(), 1); - - device_info.UnregisterScreenLockedListener("listener_2"); - EXPECT_EQ(device_info.screen_locked_listeners_.size(), 0); -} - -TEST(DeviceInfo, DISABLED_UpdateScreenLockedListener) { - absl::Notification notification; - - api::DeviceInfo::ScreenStatus screen_locked_tracker = - api::DeviceInfo::ScreenStatus::kUndetermined; - - std::function listener = - [&screen_locked_tracker, - ¬ification](api::DeviceInfo::ScreenStatus status) { - screen_locked_tracker = api::DeviceInfo::ScreenStatus::kLocked; - notification.Notify(); - }; - - DeviceInfo device_info; - device_info.RegisterScreenLockedListener("listener", listener); - EXPECT_TRUE(notification.WaitForNotificationWithTimeout(absl::Seconds(5))); - EXPECT_EQ(screen_locked_tracker, api::DeviceInfo::ScreenStatus::kLocked); -} - } // namespace } // namespace windows } // namespace nearby diff --git a/internal/platform/implementation/windows/session_manager.cc b/internal/platform/implementation/windows/session_manager.cc new file mode 100644 index 00000000..1c22295e --- /dev/null +++ b/internal/platform/implementation/windows/session_manager.cc @@ -0,0 +1,238 @@ +// 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/session_manager.h" + +#include +#include + +#include + +#include "absl/base/attributes.h" +#include "absl/container/flat_hash_map.h" +#include "absl/functional/any_invocable.h" +#include "absl/synchronization/mutex.h" +#include "absl/synchronization/notification.h" +#include "internal/platform/implementation/windows/submittable_executor.h" +#include "internal/platform/logging.h" + +namespace nearby { +namespace windows { +namespace { + +constexpr char kMessageWindowClass[] = "Nearby Message Window Class"; +constexpr char kMessageWindowTitle[] = "Nearby Message Dummy Window"; + +// Define global static variables. +ABSL_CONST_INIT absl::Mutex kSessionMutex(absl::kConstInit); +HWND kSessionHwnd = nullptr; +SubmittableExecutor* kSessionThread = nullptr; +absl::flat_hash_map>* + kSessionCallbacks = nullptr; + +LRESULT CALLBACK NearbyWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) { + switch (uMsg) { + case WM_DESTROY: + PostQuitMessage(0); + return 0; + case WM_WTSSESSION_CHANGE: + if (wParam == WTS_SESSION_LOCK) { + absl::MutexLock lock(&kSessionMutex); + for (auto& it : *kSessionCallbacks) { + it.second(SessionManager::SessionState::kLock); + } + } else if (wParam == WTS_SESSION_UNLOCK) { + absl::MutexLock lock(&kSessionMutex); + for (auto& it : *kSessionCallbacks) { + it.second(SessionManager::SessionState::kUnlock); + } + } + return 0; + default: + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } +} + +HWND CreateNearbyWindow() { + HINSTANCE instance = (HINSTANCE)GetModuleHandle(nullptr); + + WNDCLASS window_class = {}; + window_class.lpfnWndProc = NearbyWindowProc; + window_class.hInstance = instance; + window_class.lpszClassName = kMessageWindowClass; + + RegisterClass(&window_class); + + HWND hwnd = + CreateWindowA(kMessageWindowClass, kMessageWindowTitle, /*dwStyle=*/0, + /*X=*/0, /*Y=*/0, /*nWidth=*/0, /*nHeight=*/0, HWND_MESSAGE, + /*hMenu=*/nullptr, instance, + /*lpParam=*/nullptr); + + return hwnd; +} + +} // namespace + +SessionManager::~SessionManager() { StopSession(); } + +bool SessionManager::RegisterSessionListener( + absl::string_view listener_name, + absl::AnyInvocable callback) { + absl::MutexLock lock(&kSessionMutex); + + // Create session thread if no running thread. + if (kSessionThread == nullptr) { + absl::Notification notification; + kSessionThread = new SubmittableExecutor(); + kSessionCallbacks = new absl::flat_hash_map< + std::string, absl::AnyInvocable>(); + kSessionThread->Execute( + [this, ¬ification]() { StartSession(notification); }); + notification.WaitForNotification(); + if (kSessionThread == nullptr) { + return false; + } + } + + if (kSessionCallbacks->contains(listener_name) || + listeners_.contains(listener_name)) { + return false; + } + + kSessionCallbacks->emplace(listener_name, std::move(callback)); + listeners_.emplace(listener_name); + return true; +} + +bool SessionManager::UnregisterSessionListener( + absl::string_view listener_name) { + absl::MutexLock lock(&kSessionMutex); + if (kSessionThread == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": No running listener."; + return false; + } + if (!kSessionCallbacks->contains(listener_name) || + !listeners_.contains(listener_name)) { + NEARBY_LOGS(ERROR) << __func__ + << ": No listener with name:" << listener_name; + return false; + } + kSessionCallbacks->erase(listener_name); + listeners_.erase(listener_name); + + if (!kSessionCallbacks->empty()) { + return true; + } + + CleanUp(); + return true; +} + +bool SessionManager::IsScreenLocked() const { + DWORD session_id = WTSGetActiveConsoleSessionId(); + WTS_INFO_CLASS wts_info_class = WTSSessionInfoEx; + LPTSTR session_info_buffer = nullptr; + DWORD session_info_buffer_size_bytes = 0; + + WTSINFOEXW* wts_info = nullptr; + LONG session_state = WTS_SESSIONSTATE_UNKNOWN; + + if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, session_id, + wts_info_class, &session_info_buffer, + &session_info_buffer_size_bytes)) { + if (session_info_buffer_size_bytes > 0) { + wts_info = (WTSINFOEXW*)session_info_buffer; + if (wts_info->Level == 1) { + session_state = wts_info->Data.WTSInfoExLevel1.SessionFlags; + } + } + WTSFreeMemory(session_info_buffer); + session_info_buffer = nullptr; + } + + return (session_state == WTS_SESSIONSTATE_LOCK); +} + +void SessionManager::StartSession(absl::Notification& notification) { + kSessionHwnd = CreateNearbyWindow(); + if (kSessionHwnd == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to create session Window."; + return; + } + + if (!WTSRegisterSessionNotification(kSessionHwnd, NOTIFY_FOR_THIS_SESSION)) { + NEARBY_LOGS(ERROR) << __func__ + << ":Failed to register session notification."; + return; + } + + notification.Notify(); + + // Main message loop + MSG msg = {}; + while (GetMessage(&msg, nullptr, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if (!WTSUnRegisterSessionNotification(kSessionHwnd)) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to register session notification."; + return; + } + + if (!UnregisterClassA(/*lpClassName=*/kMessageWindowClass, + /*hInstance=*/(HINSTANCE)GetModuleHandle(nullptr))) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to unregister window class."; + } + + NEARBY_LOGS(INFO) << __func__ << ": Completed Message loop."; +} + +void SessionManager::StopSession() { + absl::MutexLock lock(&kSessionMutex); + if (kSessionThread == nullptr) { + return; + } + for (const auto& it : listeners_) { + kSessionCallbacks->erase(it); + } + + listeners_.clear(); + if (!kSessionCallbacks->empty()) { + return; + } + + CleanUp(); +} + +void SessionManager::CleanUp() { + if (kSessionHwnd != nullptr) { + // Send message to destroy message window. + PostMessageA(kSessionHwnd, WM_DESTROY, 0, 0); + } + + kSessionThread->Shutdown(); + delete kSessionThread; + delete kSessionCallbacks; + kSessionThread = nullptr; + kSessionCallbacks = nullptr; + kSessionHwnd = nullptr; +} + +} // namespace windows +} // namespace nearby diff --git a/internal/platform/implementation/windows/session_manager.h b/internal/platform/implementation/windows/session_manager.h new file mode 100644 index 00000000..06d928b7 --- /dev/null +++ b/internal/platform/implementation/windows/session_manager.h @@ -0,0 +1,57 @@ +// 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_PLATFORM_IMPLEMENTATION_WINDOWS_SESSION_MANAGER_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_SESSION_MANAGER_H_ + +#include + +#include "absl/container/flat_hash_set.h" +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" +#include "absl/synchronization/notification.h" + +namespace nearby { +namespace windows { + +// SessionManager provides methods to access/control platform session. +class SessionManager { + public: + enum class SessionState { kLock, kUnlock }; + ~SessionManager(); + + // Setups session listener. + // listener_name - Listener name. it should be unique in the SDK level. + // callback - It will be called when session state changed, such as + // lock/unlock screen. + bool RegisterSessionListener(absl::string_view listener_name, + absl::AnyInvocable callback); + + // Removes session listener by its name. + bool UnregisterSessionListener(absl::string_view listener_name); + + bool IsScreenLocked() const; + + private: + void StartSession(absl::Notification& notification); + void StopSession(); + void CleanUp(); + + absl::flat_hash_set listeners_; +}; + +} // namespace windows +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_SESSION_MANAGER_H_ From 33b2748a8c8a9e2cc1e454eefff1e36667bbdef3 Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Tue, 1 Aug 2023 15:36:51 -0700 Subject: [PATCH 049/128] Add UX for Retroactive Pairing PiperOrigin-RevId: 552944003 --- fastpair/common/fast_pair_device.h | 7 ++ fastpair/internal/fast_pair_seeker_impl.cc | 2 + fastpair/pairing/pairer_broker_impl.cc | 66 ++++++++++--------- fastpair/plugins/windows_admin_plugin.cc | 53 ++++++++++----- fastpair/plugins/windows_admin_plugin.h | 1 + .../retroactive_pairing_detector_impl.cc | 6 +- fastpair/ui/actions.h | 11 ++-- ...st_pair_notification_controller_observer.h | 13 ++-- .../fast_pair_notification_controller.cc | 10 +-- .../fast_pair_notification_controller.h | 16 ++--- .../fast_pair_notification_controller_test.cc | 22 ++++--- .../ui/fast_pair/fast_pair_presenter_impl.cc | 9 +-- .../fast_pair_presenter_impl_test.cc | 8 +-- .../mock_fast_pair_notification_controller.h | 4 +- 14 files changed, 129 insertions(+), 99 deletions(-) diff --git a/fastpair/common/fast_pair_device.h b/fastpair/common/fast_pair_device.h index be02d25f..cf255312 100644 --- a/fastpair/common/fast_pair_device.h +++ b/fastpair/common/fast_pair_device.h @@ -105,6 +105,12 @@ class FastPairDevice { return should_show_ui_notification_; } + void StartedPairing(bool started_pairing) { + has_started_pairing_ = started_pairing; + } + + bool HasStartedPairing() const { return has_started_pairing_; } + private: std::string model_id_; @@ -135,6 +141,7 @@ class FastPairDevice { std::optional metadata_; std::optional should_show_ui_notification_; + bool has_started_pairing_ = false; }; std::ostream& operator<<(std::ostream& stream, const FastPairDevice& device); diff --git a/fastpair/internal/fast_pair_seeker_impl.cc b/fastpair/internal/fast_pair_seeker_impl.cc index d0496bd4..10f7bafe 100644 --- a/fastpair/internal/fast_pair_seeker_impl.cc +++ b/fastpair/internal/fast_pair_seeker_impl.cc @@ -82,6 +82,7 @@ absl::Status FastPairSeekerImpl::StartInitialPairing( pairing_callback_ = std::make_unique(std::move(callback)); device_under_pairing_ = &const_cast(device); + device_under_pairing_->StartedPairing(true); pairer_broker_->PairDevice(*device_under_pairing_); return absl::OkStatus(); } @@ -100,6 +101,7 @@ absl::Status FastPairSeekerImpl::StartRetroactivePairing( } device_under_pairing_ = &const_cast(device); + device_under_pairing_->StartedPairing(true); controller_ = std::make_unique( &mediums_, device_under_pairing_, executor_); retroactive_pair_ = std::make_unique(controller_.get()); diff --git a/fastpair/pairing/pairer_broker_impl.cc b/fastpair/pairing/pairer_broker_impl.cc index eca58aaa..12ba5a19 100644 --- a/fastpair/pairing/pairer_broker_impl.cc +++ b/fastpair/pairing/pairer_broker_impl.cc @@ -233,42 +233,44 @@ void PairerBrokerImpl::OnFastPairDevicePaired(FastPairDevice& device) { void PairerBrokerImpl::OnFastPairPairingFailure(FastPairDevice& device, PairFailure failure) { - MutexLock lock(&mutex_); - ++pair_failure_counts_[device.GetModelId()]; - NEARBY_LOGS(INFO) << __func__ << ": Device=" << device - << ", Failure=" << failure << ", Failure Count = " - << pair_failure_counts_[device.GetModelId()]; - if (pair_failure_counts_[device.GetModelId()] == kMaxFailureRetryCount) { - if (!fast_pair_pairers_[device.GetModelId()]->IsPaired()) { - fast_pair_pairers_[device.GetModelId()]->CancelPairing(); + { + MutexLock lock(&mutex_); + ++pair_failure_counts_[device.GetModelId()]; + NEARBY_LOGS(INFO) << __func__ << ": Device=" << device + << ", Failure=" << failure << ", Failure Count = " + << pair_failure_counts_[device.GetModelId()]; + if (pair_failure_counts_[device.GetModelId()] == kMaxFailureRetryCount) { + if (!fast_pair_pairers_[device.GetModelId()]->IsPaired()) { + fast_pair_pairers_[device.GetModelId()]->CancelPairing(); + } + executor_->Execute("EraseHandshakeAndPairers", + [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { + EraseHandshakeAndPairers(device); + }); + NEARBY_LOGS(INFO) << __func__ + << ": Reached max failure count. Notifying observers."; + for (auto& observer : observers_.GetObservers()) { + observer->OnPairFailure(device, failure); + } + return; } - executor_->Execute("EraseHandshakeAndPairers", - [&]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { - EraseHandshakeAndPairers(device); - }); - NEARBY_LOGS(INFO) << __func__ - << ": Reached max failure count. Notifying observers."; - for (auto& observer : observers_.GetObservers()) { - observer->OnPairFailure(device, failure); - } - return; - } - if (!fast_pair_pairers_[device.GetModelId()]->IsPaired()) { - NEARBY_LOGS(INFO) << __func__ - << ": Cancelling pairing and scheduling retry " - "for failed pair attempt."; - fast_pair_pairers_[device.GetModelId()]->CancelPairing(); + if (!fast_pair_pairers_[device.GetModelId()]->IsPaired()) { + NEARBY_LOGS(INFO) << __func__ + << ": Cancelling pairing and scheduling retry " + "for failed pair attempt."; + fast_pair_pairers_[device.GetModelId()]->CancelPairing(); + fast_pair_pairers_.erase(device.GetModelId()); + // Create a timer to wait |kCancelPairingRetryDelay| after cancelling + // pairing to retry the pairing attempt. + cancel_pairing_timer_ = std::make_unique(); + cancel_pairing_timer_->Start( + kCancelPairingRetryDelay / absl::Milliseconds(1), 0, + [&]() { PairFastPairDevice(device); }); + return; + } fast_pair_pairers_.erase(device.GetModelId()); - // Create a timer to wait |kCancelPairingRetryDelay| after cancelling - // pairing to retry the pairing attempt. - cancel_pairing_timer_ = std::make_unique(); - cancel_pairing_timer_->Start( - kCancelPairingRetryDelay / absl::Milliseconds(1), 0, - [&]() { PairFastPairDevice(device); }); - return; } - fast_pair_pairers_.erase(device.GetModelId()); PairFastPairDevice(device); } diff --git a/fastpair/plugins/windows_admin_plugin.cc b/fastpair/plugins/windows_admin_plugin.cc index c78f4b74..4877309b 100644 --- a/fastpair/plugins/windows_admin_plugin.cc +++ b/fastpair/plugins/windows_admin_plugin.cc @@ -27,16 +27,28 @@ void WindowsAdminPlugin::PluginState::DiscoveryClicked(DiscoveryAction action) { NEARBY_LOGS(INFO) << __func__ << ": Action = kPairToDevice"; absl::Status status = fast_pair_service->GetSeeker()->StartInitialPairing( *device, InitialPairingParam{}, - {.on_pairing_result = [this](const FastPairDevice& device, - absl::Status status) { - NEARBY_LOGS(INFO) << "Pairing result: " << status; + {.on_pairing_result = [this](const FastPairDevice& callback_device, + absl::Status status) { + NEARBY_LOGS(INFO) << "Show pairing result: " << status; for (auto* observer : observers.GetObservers()) { - observer->OnPairingResult(device.GetMetadata().value(), - status.ok()); + observer->OnPairingResult( + const_cast(callback_device), status.ok()); } }}); NEARBY_LOGS(INFO) << "StartInitialPairing: " << status; } break; + case DiscoveryAction::kSaveDeviceToAccount: { + NEARBY_LOGS(INFO) << __func__ << ": Action = kSaveDeviceToAccount"; + absl::Status status = + fast_pair_service->GetSeeker()->FinishRetroactivePairing( + *device, FinishRetroactivePairingParam{.save_account_key = true}, + {.on_pairing_result = [](const FastPairDevice& device, + absl::Status status) { + NEARBY_LOGS(INFO) << "Finish retro result: " << status; + }}); + foreground_currently_showing_notification = false; + NEARBY_LOGS(INFO) << "FinishRetroactivePairing: " << status; + } break; case DiscoveryAction::kDismissedByOs: NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByOs"; break; @@ -80,32 +92,39 @@ void WindowsAdminPlugin::OnInitialDiscoveryEvent( << "Ignoring initial discovery event because metadata is missing"; return; } + if (device_->ShouldShowUiNotification().has_value() && + !device_->ShouldShowUiNotification().value()) { + NEARBY_LOGS(INFO) << __func__ << ": Ignoring because show UI flag is false"; + return; + } if (state_->foreground_currently_showing_notification) { NEARBY_LOGS(VERBOSE) << __func__ << ": Already showing a notification for a device"; return; } - // Show discovery notification - state_->foreground_currently_showing_notification = true; - - state_->device = device_; - for (auto* observer : state_->observers.GetObservers()) { - observer->OnUpdateDevice(*metadata); - } + NotifyShowNotification(*device_); } void WindowsAdminPlugin::OnPairEvent(const PairEvent& event) { NEARBY_LOGS(INFO) << "Received on pair event"; absl::Status status = seeker_->StartRetroactivePairing( *device_, RetroactivePairingParam{}, - {.on_pairing_result = [](const FastPairDevice& device, - absl::Status status) { - NEARBY_LOGS(INFO) << "Pairing result: " << status; - // TODO(jsobczak): Ask for user consent and save the Account Key to - // user's account. + {.on_pairing_result = [this](const FastPairDevice& device, + absl::Status status) { + NEARBY_LOGS(INFO) << "Retroactive Pairing result: " << status; + if (!status.ok()) return; + NotifyShowNotification(device); }}); NEARBY_LOGS(INFO) << "StartRetroactivePairing: " << status; } +void WindowsAdminPlugin::NotifyShowNotification(const FastPairDevice& device) { + NEARBY_LOGS(INFO) << __func__; + state_->foreground_currently_showing_notification = true; + state_->device = &device; + for (auto* observer : state_->observers.GetObservers()) { + observer->OnUpdateDevice(const_cast(device)); + } +} } // namespace fastpair } // namespace nearby diff --git a/fastpair/plugins/windows_admin_plugin.h b/fastpair/plugins/windows_admin_plugin.h index 6dd600cf..673ae2e2 100644 --- a/fastpair/plugins/windows_admin_plugin.h +++ b/fastpair/plugins/windows_admin_plugin.h @@ -60,6 +60,7 @@ class WindowsAdminPlugin : public FastPairPlugin { void OnPairEvent(const PairEvent& event) override; private: + void NotifyShowNotification(const FastPairDevice& device); FastPairSeeker* seeker_; const FastPairDevice* device_; PluginState* state_; diff --git a/fastpair/retroactive/retroactive_pairing_detector_impl.cc b/fastpair/retroactive/retroactive_pairing_detector_impl.cc index 7a104b94..40b4c579 100644 --- a/fastpair/retroactive/retroactive_pairing_detector_impl.cc +++ b/fastpair/retroactive/retroactive_pairing_detector_impl.cc @@ -70,9 +70,11 @@ void RetroactivePairingDetectorImpl::DevicePairedChanged( std::optional existing_device = repository_->FindDevice(device.GetMacAddress()); - if (existing_device.has_value()) { + if (existing_device.has_value() && + existing_device.value()->HasStartedPairing()) { // Both classic paired and Fast paired devices call this function, so we - // have to filter out pairing events for devices that we already know. + // have to filter out pairing events for device that paired from Fast Pair. + NEARBY_LOGS(INFO) << __func__ << ": Ignoring Fast paired devices."; return; } diff --git a/fastpair/ui/actions.h b/fastpair/ui/actions.h index a7910281..d8b85a89 100644 --- a/fastpair/ui/actions.h +++ b/fastpair/ui/actions.h @@ -21,11 +21,12 @@ namespace fastpair { enum class DiscoveryAction { kUnknown = 0, kPairToDevice = 1, - kDismissedByUser = 2, - kDismissedByOs = 3, - kLearnMore = 4, - kDone = 5, - kDismissedByTimeout = 6, + kSaveDeviceToAccount = 2, + kDismissedByUser = 3, + kDismissedByOs = 4, + kLearnMore = 5, + kDone = 6, + kDismissedByTimeout = 7, }; } // namespace fastpair diff --git a/fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h b/fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h index e494fb8d..0416beda 100644 --- a/fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h +++ b/fastpair/ui/fast_pair/fake_fast_pair_notification_controller_observer.h @@ -21,6 +21,7 @@ #include #include "fastpair/common/device_metadata.h" +#include "fastpair/common/fast_pair_device.h" #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/mutex_lock.h" @@ -37,24 +38,24 @@ class FakeFastPairNotificationControllerObserver on_pairing_result_latch_ = on_pairing_result_latch; } - void OnUpdateDevice(const DeviceMetadata& device) override { + void OnUpdateDevice(FastPairDevice& device) override { MutexLock lock(&mutex_); - device_ = &const_cast(device); + device_ = &const_cast(device); if (on_device_updated_latch_) { on_device_updated_latch_->CountDown(); } } - void OnPairingResult(const DeviceMetadata& device, bool success) override { + void OnPairingResult(FastPairDevice& device, bool success) override { MutexLock lock(&mutex_); pairing_result_ = success; - device_ = &const_cast(device); + device_ = &const_cast(device); if (on_pairing_result_latch_) { on_pairing_result_latch_->CountDown(); } } - DeviceMetadata* GetDevice() { + FastPairDevice* GetDevice() { MutexLock lock(&mutex_); return device_; } @@ -68,7 +69,7 @@ class FakeFastPairNotificationControllerObserver Mutex mutex_; CountDownLatch* on_device_updated_latch_; CountDownLatch* on_pairing_result_latch_; - DeviceMetadata* device_ ABSL_GUARDED_BY(mutex_) = nullptr; + FastPairDevice* device_ ABSL_GUARDED_BY(mutex_) = nullptr; std::optional pairing_result_ ABSL_GUARDED_BY(mutex_); }; } // namespace fastpair diff --git a/fastpair/ui/fast_pair/fast_pair_notification_controller.cc b/fastpair/ui/fast_pair/fast_pair_notification_controller.cc index f19396dd..60aa5b51 100644 --- a/fastpair/ui/fast_pair/fast_pair_notification_controller.cc +++ b/fastpair/ui/fast_pair/fast_pair_notification_controller.cc @@ -17,7 +17,7 @@ #include #include "absl/functional/any_invocable.h" -#include "fastpair/common/device_metadata.h" +#include "fastpair/common/fast_pair_device.h" #include "fastpair/ui/actions.h" #include "internal/platform/logging.h" @@ -32,7 +32,7 @@ void FastPairNotificationController::RemoveObserver(Observer* observer) { } void FastPairNotificationController::NotifyShowDiscovery( - const DeviceMetadata& device) { + FastPairDevice& device) { NEARBY_LOGS(INFO) << __func__; for (Observer* observer : observers_.GetObservers()) { observer->OnUpdateDevice(device); @@ -40,7 +40,7 @@ void FastPairNotificationController::NotifyShowDiscovery( } void FastPairNotificationController::NotifyShowPairingResult( - const DeviceMetadata& device, bool success) { + FastPairDevice& device, bool success) { NEARBY_LOGS(INFO) << __func__; for (Observer* observer : observers_.GetObservers()) { observer->OnPairingResult(device, success); @@ -48,14 +48,14 @@ void FastPairNotificationController::NotifyShowPairingResult( } void FastPairNotificationController::ShowGuestDiscoveryNotification( - const DeviceMetadata& device, DiscoveryCallback callback) { + FastPairDevice& device, DiscoveryCallback callback) { callback_ = std::move(callback); NEARBY_LOGS(INFO) << __func__ << "Notify show guest discovery notification. "; NotifyShowDiscovery(device); } void FastPairNotificationController::ShowPairingResultNotification( - const DeviceMetadata& device, bool success) { + FastPairDevice& device, bool success) { NEARBY_LOGS(INFO) << __func__ << "Notify show pairing result notification. "; NotifyShowPairingResult(device, success); } diff --git a/fastpair/ui/fast_pair/fast_pair_notification_controller.h b/fastpair/ui/fast_pair/fast_pair_notification_controller.h index 7978ee4d..617304ca 100644 --- a/fastpair/ui/fast_pair/fast_pair_notification_controller.h +++ b/fastpair/ui/fast_pair/fast_pair_notification_controller.h @@ -16,8 +16,8 @@ #define THIRD_PARTY_NEARBY_FASTPAIR_UI_FAST_PAIR_FAST_PAIR_NOTIFICATION_CONTROLLER_H_ #include "absl/functional/any_invocable.h" -#include "absl/strings/string_view.h" #include "fastpair/common/device_metadata.h" +#include "fastpair/common/fast_pair_device.h" #include "fastpair/ui/actions.h" #include "internal/base/observer_list.h" @@ -38,9 +38,8 @@ class FastPairNotificationController { class Observer { public: virtual ~Observer() = default; - virtual void OnUpdateDevice(const DeviceMetadata& device) = 0; - virtual void OnPairingResult(const DeviceMetadata& device, - bool success) = 0; + virtual void OnUpdateDevice(FastPairDevice& device) = 0; + virtual void OnPairingResult(FastPairDevice& device, bool success) = 0; }; FastPairNotificationController() = default; @@ -53,15 +52,14 @@ class FastPairNotificationController { // Observer process void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); - void NotifyShowDiscovery(const DeviceMetadata& device); - void NotifyShowPairingResult(const DeviceMetadata& device, bool success); + void NotifyShowDiscovery(FastPairDevice& device); + void NotifyShowPairingResult(FastPairDevice& device, bool success); // Creates and displays corresponding notification. - void ShowGuestDiscoveryNotification(const DeviceMetadata& device_metadata, + void ShowGuestDiscoveryNotification(FastPairDevice& device, DiscoveryCallback callback); - void ShowPairingResultNotification(const DeviceMetadata& device_metadata, - bool success); + void ShowPairingResultNotification(FastPairDevice& device, bool success); // Triggers callback when the related action is clicked. void OnDiscoveryClicked(DiscoveryAction action); diff --git a/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc b/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc index 081f6f79..a4170969 100644 --- a/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc +++ b/fastpair/ui/fast_pair/fast_pair_notification_controller_test.cc @@ -14,13 +14,8 @@ #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" -#include #include -#include -#include -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "fastpair/common/device_metadata.h" #include "fastpair/ui/actions.h" @@ -32,14 +27,18 @@ namespace fastpair { namespace { const int64_t kDeviceId = 10148625; -const char kModelId[] = "9adb11"; const char kDeviceName[] = "Pixel Buds Pro"; +constexpr absl::string_view kModelId("9adb11"); +constexpr absl::string_view kBleAddress("AA:BB:CC:DD:EE:00"); TEST(FastPairNotificationControllerTest, ShowGuestDiscoveryNotification) { FastPairNotificationController notification_controller; proto::GetObservedDeviceResponse response; DeviceMetadata device_metadata(response); + FastPairDevice device(kModelId, kBleAddress, + Protocol::kFastPairInitialPairing); + device.SetMetadata(device_metadata); CountDownLatch on_update_device_latch(1); CountDownLatch on_click_latch(1); FakeFastPairNotificationControllerObserver observer(&on_update_device_latch, @@ -48,12 +47,12 @@ TEST(FastPairNotificationControllerTest, ShowGuestDiscoveryNotification) { EXPECT_EQ(observer.GetDevice(), nullptr); DiscoveryAction discovery_action = DiscoveryAction::kUnknown; notification_controller.ShowGuestDiscoveryNotification( - device_metadata, [&](DiscoveryAction action) { + device, [&](DiscoveryAction action) { on_click_latch.CountDown(); discovery_action = action; }); on_update_device_latch.Await(); - EXPECT_EQ(observer.GetDevice(), &device_metadata); + EXPECT_EQ(observer.GetDevice(), &device); notification_controller.OnDiscoveryClicked(DiscoveryAction::kPairToDevice); on_click_latch.Await(); EXPECT_EQ(discovery_action, DiscoveryAction::kPairToDevice); @@ -63,6 +62,9 @@ TEST(FastPairNotificationControllerTest, ShowPairingResultNotification) { FastPairNotificationController notification_controller; proto::GetObservedDeviceResponse response; DeviceMetadata device_metadata(response); + FastPairDevice device(kModelId, kBleAddress, + Protocol::kFastPairInitialPairing); + device.SetMetadata(device_metadata); CountDownLatch on_pairing_result_latch(1); FakeFastPairNotificationControllerObserver observer(nullptr, @@ -70,9 +72,9 @@ TEST(FastPairNotificationControllerTest, ShowPairingResultNotification) { notification_controller.AddObserver(&observer); EXPECT_FALSE(observer.GetPairingResult().has_value()); EXPECT_EQ(observer.GetDevice(), nullptr); - notification_controller.ShowPairingResultNotification(device_metadata, true); + notification_controller.ShowPairingResultNotification(device, true); on_pairing_result_latch.Await(); - EXPECT_EQ(observer.GetDevice(), &device_metadata); + EXPECT_EQ(observer.GetDevice(), &device); EXPECT_TRUE(observer.GetPairingResult().has_value()); EXPECT_TRUE(observer.GetPairingResult().value()); } diff --git a/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc b/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc index 6b9ff70c..7a5d98e5 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc +++ b/fastpair/ui/fast_pair/fast_pair_presenter_impl.cc @@ -15,14 +15,10 @@ #include "fastpair/ui/fast_pair/fast_pair_presenter_impl.h" #include -#include #include -#include "fastpair/common/device_metadata.h" #include "fastpair/common/fast_pair_device.h" -#include "fastpair/repository/fast_pair_repository.h" #include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" -#include "internal/platform/logging.h" namespace nearby { namespace fastpair { @@ -51,15 +47,14 @@ void FastPairPresenterImpl::ShowDiscovery( FastPairDevice& device, FastPairNotificationController& notification_controller, DiscoveryCallback callback) { - notification_controller.ShowGuestDiscoveryNotification(*device.GetMetadata(), + notification_controller.ShowGuestDiscoveryNotification(device, std::move(callback)); } void FastPairPresenterImpl::ShowPairingResult( FastPairDevice& device, FastPairNotificationController& notification_controller, bool success) { - notification_controller.ShowPairingResultNotification(*device.GetMetadata(), - success); + notification_controller.ShowPairingResultNotification(device, success); } } // namespace fastpair } // namespace nearby diff --git a/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc b/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc index 221004e5..d3a00c5c 100644 --- a/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc +++ b/fastpair/ui/fast_pair/fast_pair_presenter_impl_test.cc @@ -72,9 +72,9 @@ TEST(FastPairPresenterImplTest, ShowDiscovery) { discovery_action = action; }); on_update_device_latch.Await(); - EXPECT_EQ(observer.GetDevice()->GetFastPairVersion(), + EXPECT_EQ(observer.GetDevice()->GetMetadata()->GetFastPairVersion(), DeviceFastPairVersion::kV1); - EXPECT_THAT(observer.GetDevice()->GetResponse(), + EXPECT_THAT(observer.GetDevice()->GetMetadata()->GetResponse(), MatchesProto(response_proto)); } @@ -101,9 +101,9 @@ TEST(FastPairPresenterImplTest, ShowPairingResult) { fast_pair_presenter.ShowPairingResult(fast_pair_device, notification_controller, true); on_pairing_result_latch.Await(); - EXPECT_EQ(observer.GetDevice()->GetFastPairVersion(), + EXPECT_EQ(observer.GetDevice()->GetMetadata()->GetFastPairVersion(), DeviceFastPairVersion::kV1); - EXPECT_THAT(observer.GetDevice()->GetResponse(), + EXPECT_THAT(observer.GetDevice()->GetMetadata()->GetResponse(), MatchesProto(response_proto)); EXPECT_TRUE(observer.GetPairingResult().has_value()); EXPECT_TRUE(observer.GetPairingResult().value()); diff --git a/fastpair/ui/fast_pair/mock_fast_pair_notification_controller.h b/fastpair/ui/fast_pair/mock_fast_pair_notification_controller.h index 84b72603..e8427c57 100644 --- a/fastpair/ui/fast_pair/mock_fast_pair_notification_controller.h +++ b/fastpair/ui/fast_pair/mock_fast_pair_notification_controller.h @@ -26,12 +26,12 @@ class MockFastPairNotificationController : public FastPairNotificationController { public: MOCK_METHOD(void, ShowGuestDiscoveryNotification, - (const DeviceMetadata&, DiscoveryCallback)); + (FastPairDevice & device, DiscoveryCallback)); MOCK_METHOD(void, OnDiscoveryClicked, (DiscoveryAction)); MOCK_METHOD(void, AddObserver, (Observer*)); MOCK_METHOD(void, RemoveObserver, (Observer*)); - void NotifyShowDiscovery(const DeviceMetadata& device) { + void NotifyShowDiscovery(FastPairDevice& device) { for (Observer* observer : observers_.GetObservers()) { observer->OnUpdateDevice(device); } From 20f2291476a68b7ca5cd4f337e3f0eb8669adcdc Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Tue, 1 Aug 2023 16:25:49 -0700 Subject: [PATCH 050/128] Added APIs to allow/prevent sleep in session manager PiperOrigin-RevId: 552958620 --- .../implementation/windows/session_manager.cc | 21 +++++++++++++++++++ .../implementation/windows/session_manager.h | 6 ++++++ 2 files changed, 27 insertions(+) diff --git a/internal/platform/implementation/windows/session_manager.cc b/internal/platform/implementation/windows/session_manager.cc index 1c22295e..09c207b9 100644 --- a/internal/platform/implementation/windows/session_manager.cc +++ b/internal/platform/implementation/windows/session_manager.cc @@ -167,6 +167,27 @@ bool SessionManager::IsScreenLocked() const { return (session_state == WTS_SESSIONSTATE_LOCK); } +bool SessionManager::PreventSleep() const { + EXECUTION_STATE execution_state = + SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED); + if (execution_state == 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to set execution state of the thread."; + return false; + } + return true; +} + +bool SessionManager::AllowSleep() const { + EXECUTION_STATE execution_state = SetThreadExecutionState(ES_CONTINUOUS); + if (execution_state == 0) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to set execution state of the thread."; + return false; + } + return true; +} + void SessionManager::StartSession(absl::Notification& notification) { kSessionHwnd = CreateNearbyWindow(); if (kSessionHwnd == nullptr) { diff --git a/internal/platform/implementation/windows/session_manager.h b/internal/platform/implementation/windows/session_manager.h index 06d928b7..a5da2c26 100644 --- a/internal/platform/implementation/windows/session_manager.h +++ b/internal/platform/implementation/windows/session_manager.h @@ -43,6 +43,12 @@ class SessionManager { bool IsScreenLocked() const; + // Prevents the Windows device from sleeping state. + bool PreventSleep() const; + + // Allows the Windows device to sleep state. + bool AllowSleep() const; + private: void StartSession(absl::Notification& notification); void StopSession(); From 8904cb04a64b33d5b9c201481f8542195e1f09c1 Mon Sep 17 00:00:00 2001 From: hai007 Date: Tue, 1 Aug 2023 22:19:32 -0700 Subject: [PATCH 051/128] [Sharing] Add SetAccount and activity name logging when user switches account. PiperOrigin-RevId: 553027327 --- proto/sharing_enums.proto | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index f34d8f7e..abb3478d 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -30,7 +30,7 @@ option objc_class_prefix = "GNSHP"; // in NearbyClearcutLogger (for android, or clearcut_event_logger as the // equivalence for Windows) for all events (may exclude settings), and // session_id for a pair of events (start and end of a session). -// Next id: 63 +// Next id: 64 enum EventType { UNKNOWN_EVENT_TYPE = 0; @@ -232,6 +232,9 @@ enum EventType { // Send desktop notification. SEND_DESKTOP_NOTIFICATION = 62; + // User sets account preference + SET_ACCOUNT = 63; + // LINT.ThenChange(//depot/google3/location/nearby/proto/nearby_event_codes.proto:SharingEventCode) } From 347d10b74f4b57d8fc832ccc1b9366b7fa298291 Mon Sep 17 00:00:00 2001 From: hai007 Date: Wed, 2 Aug 2023 07:29:16 -0700 Subject: [PATCH 052/128] Automated visibility attribute cleanup. PiperOrigin-RevId: 553138950 --- internal/platform/BUILD | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 31aa5857..fc4597a4 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -49,7 +49,6 @@ cc_library( "//connections:__subpackages__", "//fastpair:__subpackages__", "//internal/auth:__subpackages__", - "//internal/interop:__pkg__", "//internal/platform:__subpackages__", "//internal/platform/implementation:__subpackages__", "//internal/preferences:__subpackages__", @@ -107,7 +106,6 @@ cc_library( "//internal/auth:__subpackages__", "//internal/auth/credential_store:__subpackages__", "//internal/data:__subpackages__", - "//internal/interop:__pkg__", "//internal/network:__subpackages__", "//internal/platform:__subpackages__", "//internal/proto/analytics:__subpackages__", From 20e55d6d789eee4b90757f550fd9e11fe940261a Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 28 Jul 2023 15:38:06 -0700 Subject: [PATCH 053/128] [fp-rs] Migrating from fastpair/rust to fastpair/rust/bluetooth. --- fastpair/rust/{ => bluetooth}/Cargo.toml | 0 fastpair/rust/{ => bluetooth}/rustfmt.toml | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/api/adapter.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/api/device.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/api/mod.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/common/address.rs | 0 .../rust/{ => bluetooth}/src/bluetooth/common/advertisement.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/common/error.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/common/mod.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/mod.rs | 0 .../rust/{ => bluetooth}/src/bluetooth/unsupported/adapter.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/unsupported/device.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/unsupported/mod.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/windows/adapter.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/windows/address.rs | 0 .../rust/{ => bluetooth}/src/bluetooth/windows/advertisement.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/windows/device.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/windows/error.rs | 0 fastpair/rust/{ => bluetooth}/src/bluetooth/windows/mod.rs | 0 fastpair/rust/{ => bluetooth}/src/lib.rs | 0 fastpair/rust/{ => bluetooth}/src/main.rs | 0 fastpair/rust/{ => bluetooth}/src/message_stream.rs | 0 fastpair/rust/{ => bluetooth}/src/types.rs | 0 fastpair/rust/{ => bluetooth}/src/types/packets.rs | 0 fastpair/rust/{ => bluetooth}/tests/integration_test.rs | 0 25 files changed, 0 insertions(+), 0 deletions(-) rename fastpair/rust/{ => bluetooth}/Cargo.toml (100%) rename fastpair/rust/{ => bluetooth}/rustfmt.toml (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/api/adapter.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/api/device.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/api/mod.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/common/address.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/common/advertisement.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/common/error.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/common/mod.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/mod.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/unsupported/adapter.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/unsupported/device.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/unsupported/mod.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/windows/adapter.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/windows/address.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/windows/advertisement.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/windows/device.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/windows/error.rs (100%) rename fastpair/rust/{ => bluetooth}/src/bluetooth/windows/mod.rs (100%) rename fastpair/rust/{ => bluetooth}/src/lib.rs (100%) rename fastpair/rust/{ => bluetooth}/src/main.rs (100%) rename fastpair/rust/{ => bluetooth}/src/message_stream.rs (100%) rename fastpair/rust/{ => bluetooth}/src/types.rs (100%) rename fastpair/rust/{ => bluetooth}/src/types/packets.rs (100%) rename fastpair/rust/{ => bluetooth}/tests/integration_test.rs (100%) diff --git a/fastpair/rust/Cargo.toml b/fastpair/rust/bluetooth/Cargo.toml similarity index 100% rename from fastpair/rust/Cargo.toml rename to fastpair/rust/bluetooth/Cargo.toml diff --git a/fastpair/rust/rustfmt.toml b/fastpair/rust/bluetooth/rustfmt.toml similarity index 100% rename from fastpair/rust/rustfmt.toml rename to fastpair/rust/bluetooth/rustfmt.toml diff --git a/fastpair/rust/src/bluetooth/api/adapter.rs b/fastpair/rust/bluetooth/src/bluetooth/api/adapter.rs similarity index 100% rename from fastpair/rust/src/bluetooth/api/adapter.rs rename to fastpair/rust/bluetooth/src/bluetooth/api/adapter.rs diff --git a/fastpair/rust/src/bluetooth/api/device.rs b/fastpair/rust/bluetooth/src/bluetooth/api/device.rs similarity index 100% rename from fastpair/rust/src/bluetooth/api/device.rs rename to fastpair/rust/bluetooth/src/bluetooth/api/device.rs diff --git a/fastpair/rust/src/bluetooth/api/mod.rs b/fastpair/rust/bluetooth/src/bluetooth/api/mod.rs similarity index 100% rename from fastpair/rust/src/bluetooth/api/mod.rs rename to fastpair/rust/bluetooth/src/bluetooth/api/mod.rs diff --git a/fastpair/rust/src/bluetooth/common/address.rs b/fastpair/rust/bluetooth/src/bluetooth/common/address.rs similarity index 100% rename from fastpair/rust/src/bluetooth/common/address.rs rename to fastpair/rust/bluetooth/src/bluetooth/common/address.rs diff --git a/fastpair/rust/src/bluetooth/common/advertisement.rs b/fastpair/rust/bluetooth/src/bluetooth/common/advertisement.rs similarity index 100% rename from fastpair/rust/src/bluetooth/common/advertisement.rs rename to fastpair/rust/bluetooth/src/bluetooth/common/advertisement.rs diff --git a/fastpair/rust/src/bluetooth/common/error.rs b/fastpair/rust/bluetooth/src/bluetooth/common/error.rs similarity index 100% rename from fastpair/rust/src/bluetooth/common/error.rs rename to fastpair/rust/bluetooth/src/bluetooth/common/error.rs diff --git a/fastpair/rust/src/bluetooth/common/mod.rs b/fastpair/rust/bluetooth/src/bluetooth/common/mod.rs similarity index 100% rename from fastpair/rust/src/bluetooth/common/mod.rs rename to fastpair/rust/bluetooth/src/bluetooth/common/mod.rs diff --git a/fastpair/rust/src/bluetooth/mod.rs b/fastpair/rust/bluetooth/src/bluetooth/mod.rs similarity index 100% rename from fastpair/rust/src/bluetooth/mod.rs rename to fastpair/rust/bluetooth/src/bluetooth/mod.rs diff --git a/fastpair/rust/src/bluetooth/unsupported/adapter.rs b/fastpair/rust/bluetooth/src/bluetooth/unsupported/adapter.rs similarity index 100% rename from fastpair/rust/src/bluetooth/unsupported/adapter.rs rename to fastpair/rust/bluetooth/src/bluetooth/unsupported/adapter.rs diff --git a/fastpair/rust/src/bluetooth/unsupported/device.rs b/fastpair/rust/bluetooth/src/bluetooth/unsupported/device.rs similarity index 100% rename from fastpair/rust/src/bluetooth/unsupported/device.rs rename to fastpair/rust/bluetooth/src/bluetooth/unsupported/device.rs diff --git a/fastpair/rust/src/bluetooth/unsupported/mod.rs b/fastpair/rust/bluetooth/src/bluetooth/unsupported/mod.rs similarity index 100% rename from fastpair/rust/src/bluetooth/unsupported/mod.rs rename to fastpair/rust/bluetooth/src/bluetooth/unsupported/mod.rs diff --git a/fastpair/rust/src/bluetooth/windows/adapter.rs b/fastpair/rust/bluetooth/src/bluetooth/windows/adapter.rs similarity index 100% rename from fastpair/rust/src/bluetooth/windows/adapter.rs rename to fastpair/rust/bluetooth/src/bluetooth/windows/adapter.rs diff --git a/fastpair/rust/src/bluetooth/windows/address.rs b/fastpair/rust/bluetooth/src/bluetooth/windows/address.rs similarity index 100% rename from fastpair/rust/src/bluetooth/windows/address.rs rename to fastpair/rust/bluetooth/src/bluetooth/windows/address.rs diff --git a/fastpair/rust/src/bluetooth/windows/advertisement.rs b/fastpair/rust/bluetooth/src/bluetooth/windows/advertisement.rs similarity index 100% rename from fastpair/rust/src/bluetooth/windows/advertisement.rs rename to fastpair/rust/bluetooth/src/bluetooth/windows/advertisement.rs diff --git a/fastpair/rust/src/bluetooth/windows/device.rs b/fastpair/rust/bluetooth/src/bluetooth/windows/device.rs similarity index 100% rename from fastpair/rust/src/bluetooth/windows/device.rs rename to fastpair/rust/bluetooth/src/bluetooth/windows/device.rs diff --git a/fastpair/rust/src/bluetooth/windows/error.rs b/fastpair/rust/bluetooth/src/bluetooth/windows/error.rs similarity index 100% rename from fastpair/rust/src/bluetooth/windows/error.rs rename to fastpair/rust/bluetooth/src/bluetooth/windows/error.rs diff --git a/fastpair/rust/src/bluetooth/windows/mod.rs b/fastpair/rust/bluetooth/src/bluetooth/windows/mod.rs similarity index 100% rename from fastpair/rust/src/bluetooth/windows/mod.rs rename to fastpair/rust/bluetooth/src/bluetooth/windows/mod.rs diff --git a/fastpair/rust/src/lib.rs b/fastpair/rust/bluetooth/src/lib.rs similarity index 100% rename from fastpair/rust/src/lib.rs rename to fastpair/rust/bluetooth/src/lib.rs diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/bluetooth/src/main.rs similarity index 100% rename from fastpair/rust/src/main.rs rename to fastpair/rust/bluetooth/src/main.rs diff --git a/fastpair/rust/src/message_stream.rs b/fastpair/rust/bluetooth/src/message_stream.rs similarity index 100% rename from fastpair/rust/src/message_stream.rs rename to fastpair/rust/bluetooth/src/message_stream.rs diff --git a/fastpair/rust/src/types.rs b/fastpair/rust/bluetooth/src/types.rs similarity index 100% rename from fastpair/rust/src/types.rs rename to fastpair/rust/bluetooth/src/types.rs diff --git a/fastpair/rust/src/types/packets.rs b/fastpair/rust/bluetooth/src/types/packets.rs similarity index 100% rename from fastpair/rust/src/types/packets.rs rename to fastpair/rust/bluetooth/src/types/packets.rs diff --git a/fastpair/rust/tests/integration_test.rs b/fastpair/rust/bluetooth/tests/integration_test.rs similarity index 100% rename from fastpair/rust/tests/integration_test.rs rename to fastpair/rust/bluetooth/tests/integration_test.rs From c45dd7467dfabdb80cda77d67e138ca36efe41c7 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sat, 29 Jul 2023 15:27:05 -0700 Subject: [PATCH 054/128] [fp-rs] Initial flutter create commit --- fastpair/rust/demo/.gitignore | 44 ++ fastpair/rust/demo/.metadata | 45 ++ fastpair/rust/demo/README.md | 16 + fastpair/rust/demo/analysis_options.yaml | 29 + fastpair/rust/demo/android/.gitignore | 13 + fastpair/rust/demo/android/app/build.gradle | 72 ++ .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 33 + .../kotlin/com/example/demo/MainActivity.kt | 6 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + fastpair/rust/demo/android/build.gradle | 31 + fastpair/rust/demo/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + fastpair/rust/demo/android/settings.gradle | 11 + fastpair/rust/demo/ios/.gitignore | 34 + .../demo/ios/Flutter/AppFrameworkInfo.plist | 26 + fastpair/rust/demo/ios/Flutter/Debug.xcconfig | 1 + .../rust/demo/ios/Flutter/Release.xcconfig | 1 + .../demo/ios/Runner.xcodeproj/project.pbxproj | 613 +++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../rust/demo/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 +++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + fastpair/rust/demo/ios/Runner/Info.plist | 51 ++ .../demo/ios/Runner/Runner-Bridging-Header.h | 1 + .../demo/ios/RunnerTests/RunnerTests.swift | 12 + fastpair/rust/demo/lib/main.dart | 125 ++++ fastpair/rust/demo/linux/.gitignore | 1 + fastpair/rust/demo/linux/CMakeLists.txt | 139 ++++ .../rust/demo/linux/flutter/CMakeLists.txt | 88 +++ .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + .../linux/flutter/generated_plugins.cmake | 23 + fastpair/rust/demo/linux/main.cc | 6 + fastpair/rust/demo/linux/my_application.cc | 104 +++ fastpair/rust/demo/linux/my_application.h | 18 + fastpair/rust/demo/macos/.gitignore | 7 + .../demo/macos/Flutter/Flutter-Debug.xcconfig | 1 + .../macos/Flutter/Flutter-Release.xcconfig | 1 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + .../macos/Runner.xcodeproj/project.pbxproj | 695 ++++++++++++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 +++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../rust/demo/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 ++ .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes .../demo/macos/Runner/Base.lproj/MainMenu.xib | 343 +++++++++ .../macos/Runner/Configs/AppInfo.xcconfig | 14 + .../demo/macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 12 + fastpair/rust/demo/macos/Runner/Info.plist | 32 + .../demo/macos/Runner/MainFlutterWindow.swift | 15 + .../demo/macos/Runner/Release.entitlements | 8 + .../demo/macos/RunnerTests/RunnerTests.swift | 12 + fastpair/rust/demo/pubspec.lock | 188 +++++ fastpair/rust/demo/pubspec.yaml | 90 +++ fastpair/rust/demo/test/widget_test.dart | 30 + fastpair/rust/demo/web/favicon.png | Bin 0 -> 917 bytes fastpair/rust/demo/web/icons/Icon-192.png | Bin 0 -> 5292 bytes fastpair/rust/demo/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../rust/demo/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../rust/demo/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes fastpair/rust/demo/web/index.html | 59 ++ fastpair/rust/demo/web/manifest.json | 35 + fastpair/rust/demo/windows/.gitignore | 17 + fastpair/rust/demo/windows/CMakeLists.txt | 102 +++ .../rust/demo/windows/flutter/CMakeLists.txt | 104 +++ .../flutter/generated_plugin_registrant.cc | 11 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 23 + .../rust/demo/windows/runner/CMakeLists.txt | 40 + fastpair/rust/demo/windows/runner/Runner.rc | 121 +++ .../demo/windows/runner/flutter_window.cpp | 66 ++ .../rust/demo/windows/runner/flutter_window.h | 33 + fastpair/rust/demo/windows/runner/main.cpp | 43 ++ fastpair/rust/demo/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../demo/windows/runner/runner.exe.manifest | 20 + fastpair/rust/demo/windows/runner/utils.cpp | 65 ++ fastpair/rust/demo/windows/runner/utils.h | 19 + .../rust/demo/windows/runner/win32_window.cpp | 288 ++++++++ .../rust/demo/windows/runner/win32_window.h | 102 +++ 128 files changed, 4873 insertions(+) create mode 100644 fastpair/rust/demo/.gitignore create mode 100644 fastpair/rust/demo/.metadata create mode 100644 fastpair/rust/demo/README.md create mode 100644 fastpair/rust/demo/analysis_options.yaml create mode 100644 fastpair/rust/demo/android/.gitignore create mode 100644 fastpair/rust/demo/android/app/build.gradle create mode 100644 fastpair/rust/demo/android/app/src/debug/AndroidManifest.xml create mode 100644 fastpair/rust/demo/android/app/src/main/AndroidManifest.xml create mode 100644 fastpair/rust/demo/android/app/src/main/kotlin/com/example/demo/MainActivity.kt create mode 100644 fastpair/rust/demo/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 fastpair/rust/demo/android/app/src/main/res/drawable/launch_background.xml create mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 fastpair/rust/demo/android/app/src/main/res/values-night/styles.xml create mode 100644 fastpair/rust/demo/android/app/src/main/res/values/styles.xml create mode 100644 fastpair/rust/demo/android/app/src/profile/AndroidManifest.xml create mode 100644 fastpair/rust/demo/android/build.gradle create mode 100644 fastpair/rust/demo/android/gradle.properties create mode 100644 fastpair/rust/demo/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 fastpair/rust/demo/android/settings.gradle create mode 100644 fastpair/rust/demo/ios/.gitignore create mode 100644 fastpair/rust/demo/ios/Flutter/AppFrameworkInfo.plist create mode 100644 fastpair/rust/demo/ios/Flutter/Debug.xcconfig create mode 100644 fastpair/rust/demo/ios/Flutter/Release.xcconfig create mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/project.pbxproj create mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 fastpair/rust/demo/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 fastpair/rust/demo/ios/Runner/AppDelegate.swift create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 fastpair/rust/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 fastpair/rust/demo/ios/Runner/Base.lproj/Main.storyboard create mode 100644 fastpair/rust/demo/ios/Runner/Info.plist create mode 100644 fastpair/rust/demo/ios/Runner/Runner-Bridging-Header.h create mode 100644 fastpair/rust/demo/ios/RunnerTests/RunnerTests.swift create mode 100644 fastpair/rust/demo/lib/main.dart create mode 100644 fastpair/rust/demo/linux/.gitignore create mode 100644 fastpair/rust/demo/linux/CMakeLists.txt create mode 100644 fastpair/rust/demo/linux/flutter/CMakeLists.txt create mode 100644 fastpair/rust/demo/linux/flutter/generated_plugin_registrant.cc create mode 100644 fastpair/rust/demo/linux/flutter/generated_plugin_registrant.h create mode 100644 fastpair/rust/demo/linux/flutter/generated_plugins.cmake create mode 100644 fastpair/rust/demo/linux/main.cc create mode 100644 fastpair/rust/demo/linux/my_application.cc create mode 100644 fastpair/rust/demo/linux/my_application.h create mode 100644 fastpair/rust/demo/macos/.gitignore create mode 100644 fastpair/rust/demo/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 fastpair/rust/demo/macos/Flutter/Flutter-Release.xcconfig create mode 100644 fastpair/rust/demo/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 fastpair/rust/demo/macos/Runner.xcodeproj/project.pbxproj create mode 100644 fastpair/rust/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 fastpair/rust/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 fastpair/rust/demo/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 fastpair/rust/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 fastpair/rust/demo/macos/Runner/AppDelegate.swift create mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 fastpair/rust/demo/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 fastpair/rust/demo/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 fastpair/rust/demo/macos/Runner/Configs/Debug.xcconfig create mode 100644 fastpair/rust/demo/macos/Runner/Configs/Release.xcconfig create mode 100644 fastpair/rust/demo/macos/Runner/Configs/Warnings.xcconfig create mode 100644 fastpair/rust/demo/macos/Runner/DebugProfile.entitlements create mode 100644 fastpair/rust/demo/macos/Runner/Info.plist create mode 100644 fastpair/rust/demo/macos/Runner/MainFlutterWindow.swift create mode 100644 fastpair/rust/demo/macos/Runner/Release.entitlements create mode 100644 fastpair/rust/demo/macos/RunnerTests/RunnerTests.swift create mode 100644 fastpair/rust/demo/pubspec.lock create mode 100644 fastpair/rust/demo/pubspec.yaml create mode 100644 fastpair/rust/demo/test/widget_test.dart create mode 100644 fastpair/rust/demo/web/favicon.png create mode 100644 fastpair/rust/demo/web/icons/Icon-192.png create mode 100644 fastpair/rust/demo/web/icons/Icon-512.png create mode 100644 fastpair/rust/demo/web/icons/Icon-maskable-192.png create mode 100644 fastpair/rust/demo/web/icons/Icon-maskable-512.png create mode 100644 fastpair/rust/demo/web/index.html create mode 100644 fastpair/rust/demo/web/manifest.json create mode 100644 fastpair/rust/demo/windows/.gitignore create mode 100644 fastpair/rust/demo/windows/CMakeLists.txt create mode 100644 fastpair/rust/demo/windows/flutter/CMakeLists.txt create mode 100644 fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc create mode 100644 fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h create mode 100644 fastpair/rust/demo/windows/flutter/generated_plugins.cmake create mode 100644 fastpair/rust/demo/windows/runner/CMakeLists.txt create mode 100644 fastpair/rust/demo/windows/runner/Runner.rc create mode 100644 fastpair/rust/demo/windows/runner/flutter_window.cpp create mode 100644 fastpair/rust/demo/windows/runner/flutter_window.h create mode 100644 fastpair/rust/demo/windows/runner/main.cpp create mode 100644 fastpair/rust/demo/windows/runner/resource.h create mode 100644 fastpair/rust/demo/windows/runner/resources/app_icon.ico create mode 100644 fastpair/rust/demo/windows/runner/runner.exe.manifest create mode 100644 fastpair/rust/demo/windows/runner/utils.cpp create mode 100644 fastpair/rust/demo/windows/runner/utils.h create mode 100644 fastpair/rust/demo/windows/runner/win32_window.cpp create mode 100644 fastpair/rust/demo/windows/runner/win32_window.h diff --git a/fastpair/rust/demo/.gitignore b/fastpair/rust/demo/.gitignore new file mode 100644 index 00000000..24476c5d --- /dev/null +++ b/fastpair/rust/demo/.gitignore @@ -0,0 +1,44 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/fastpair/rust/demo/.metadata b/fastpair/rust/demo/.metadata new file mode 100644 index 00000000..de745e4a --- /dev/null +++ b/fastpair/rust/demo/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled. + +version: + revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + channel: stable + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: android + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: ios + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: linux + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: macos + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: web + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + - platform: windows + create_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + base_revision: f468f3366c26a5092eb964a230ce7892fda8f2f8 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/fastpair/rust/demo/README.md b/fastpair/rust/demo/README.md new file mode 100644 index 00000000..dbd403a0 --- /dev/null +++ b/fastpair/rust/demo/README.md @@ -0,0 +1,16 @@ +# demo + +A new Flutter project. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/fastpair/rust/demo/analysis_options.yaml b/fastpair/rust/demo/analysis_options.yaml new file mode 100644 index 00000000..61b6c4de --- /dev/null +++ b/fastpair/rust/demo/analysis_options.yaml @@ -0,0 +1,29 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/fastpair/rust/demo/android/.gitignore b/fastpair/rust/demo/android/.gitignore new file mode 100644 index 00000000..6f568019 --- /dev/null +++ b/fastpair/rust/demo/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/fastpair/rust/demo/android/app/build.gradle b/fastpair/rust/demo/android/app/build.gradle new file mode 100644 index 00000000..96442f97 --- /dev/null +++ b/fastpair/rust/demo/android/app/build.gradle @@ -0,0 +1,72 @@ +def localProperties = new Properties() +def localPropertiesFile = rootProject.file('local.properties') +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader('UTF-8') { reader -> + localProperties.load(reader) + } +} + +def flutterRoot = localProperties.getProperty('flutter.sdk') +if (flutterRoot == null) { + throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") +} + +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') +if (flutterVersionCode == null) { + flutterVersionCode = '1' +} + +def flutterVersionName = localProperties.getProperty('flutter.versionName') +if (flutterVersionName == null) { + flutterVersionName = '1.0' +} + +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" + +android { + namespace "com.example.demo" + compileSdkVersion flutter.compileSdkVersion + ndkVersion flutter.ndkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } + + sourceSets { + main.java.srcDirs += 'src/main/kotlin' + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId "com.example.demo" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdkVersion flutter.minSdkVersion + targetSdkVersion flutter.targetSdkVersion + versionCode flutterVersionCode.toInteger() + versionName flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig signingConfigs.debug + } + } +} + +flutter { + source '../..' +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} diff --git a/fastpair/rust/demo/android/app/src/debug/AndroidManifest.xml b/fastpair/rust/demo/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000..399f6981 --- /dev/null +++ b/fastpair/rust/demo/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/fastpair/rust/demo/android/app/src/main/AndroidManifest.xml b/fastpair/rust/demo/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..81300528 --- /dev/null +++ b/fastpair/rust/demo/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/fastpair/rust/demo/android/app/src/main/kotlin/com/example/demo/MainActivity.kt b/fastpair/rust/demo/android/app/src/main/kotlin/com/example/demo/MainActivity.kt new file mode 100644 index 00000000..34b9c4c6 --- /dev/null +++ b/fastpair/rust/demo/android/app/src/main/kotlin/com/example/demo/MainActivity.kt @@ -0,0 +1,6 @@ +package com.example.demo + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() { +} diff --git a/fastpair/rust/demo/android/app/src/main/res/drawable-v21/launch_background.xml b/fastpair/rust/demo/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 00000000..f74085f3 --- /dev/null +++ b/fastpair/rust/demo/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/fastpair/rust/demo/android/app/src/main/res/drawable/launch_background.xml b/fastpair/rust/demo/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 00000000..304732f8 --- /dev/null +++ b/fastpair/rust/demo/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/fastpair/rust/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/fastpair/rust/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/fastpair/rust/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/fastpair/rust/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/fastpair/rust/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/android/app/src/main/res/values-night/styles.xml b/fastpair/rust/demo/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 00000000..06952be7 --- /dev/null +++ b/fastpair/rust/demo/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/fastpair/rust/demo/android/app/src/main/res/values/styles.xml b/fastpair/rust/demo/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..cb1ef880 --- /dev/null +++ b/fastpair/rust/demo/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/fastpair/rust/demo/android/app/src/profile/AndroidManifest.xml b/fastpair/rust/demo/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 00000000..399f6981 --- /dev/null +++ b/fastpair/rust/demo/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/fastpair/rust/demo/android/build.gradle b/fastpair/rust/demo/android/build.gradle new file mode 100644 index 00000000..f7eb7f63 --- /dev/null +++ b/fastpair/rust/demo/android/build.gradle @@ -0,0 +1,31 @@ +buildscript { + ext.kotlin_version = '1.7.10' + repositories { + google() + mavenCentral() + } + + dependencies { + classpath 'com.android.tools.build:gradle:7.3.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = '../build' +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(':app') +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/fastpair/rust/demo/android/gradle.properties b/fastpair/rust/demo/android/gradle.properties new file mode 100644 index 00000000..94adc3a3 --- /dev/null +++ b/fastpair/rust/demo/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx1536M +android.useAndroidX=true +android.enableJetifier=true diff --git a/fastpair/rust/demo/android/gradle/wrapper/gradle-wrapper.properties b/fastpair/rust/demo/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..3c472b99 --- /dev/null +++ b/fastpair/rust/demo/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/fastpair/rust/demo/android/settings.gradle b/fastpair/rust/demo/android/settings.gradle new file mode 100644 index 00000000..44e62bcf --- /dev/null +++ b/fastpair/rust/demo/android/settings.gradle @@ -0,0 +1,11 @@ +include ':app' + +def localPropertiesFile = new File(rootProject.projectDir, "local.properties") +def properties = new Properties() + +assert localPropertiesFile.exists() +localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } + +def flutterSdkPath = properties.getProperty("flutter.sdk") +assert flutterSdkPath != null, "flutter.sdk not set in local.properties" +apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/fastpair/rust/demo/ios/.gitignore b/fastpair/rust/demo/ios/.gitignore new file mode 100644 index 00000000..7a7f9873 --- /dev/null +++ b/fastpair/rust/demo/ios/.gitignore @@ -0,0 +1,34 @@ +**/dgph +*.mode1v3 +*.mode2v3 +*.moved-aside +*.pbxuser +*.perspectivev3 +**/*sync/ +.sconsign.dblite +.tags* +**/.vagrant/ +**/DerivedData/ +Icon? +**/Pods/ +**/.symlinks/ +profile +xcuserdata +**/.generated/ +Flutter/App.framework +Flutter/Flutter.framework +Flutter/Flutter.podspec +Flutter/Generated.xcconfig +Flutter/ephemeral/ +Flutter/app.flx +Flutter/app.zip +Flutter/flutter_assets/ +Flutter/flutter_export_environment.sh +ServiceDefinitions.json +Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!default.mode1v3 +!default.mode2v3 +!default.pbxuser +!default.perspectivev3 diff --git a/fastpair/rust/demo/ios/Flutter/AppFrameworkInfo.plist b/fastpair/rust/demo/ios/Flutter/AppFrameworkInfo.plist new file mode 100644 index 00000000..9625e105 --- /dev/null +++ b/fastpair/rust/demo/ios/Flutter/AppFrameworkInfo.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 11.0 + + diff --git a/fastpair/rust/demo/ios/Flutter/Debug.xcconfig b/fastpair/rust/demo/ios/Flutter/Debug.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/fastpair/rust/demo/ios/Flutter/Debug.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/fastpair/rust/demo/ios/Flutter/Release.xcconfig b/fastpair/rust/demo/ios/Flutter/Release.xcconfig new file mode 100644 index 00000000..592ceee8 --- /dev/null +++ b/fastpair/rust/demo/ios/Flutter/Release.xcconfig @@ -0,0 +1 @@ +#include "Generated.xcconfig" diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/project.pbxproj b/fastpair/rust/demo/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..660a2ade --- /dev/null +++ b/fastpair/rust/demo/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,613 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C807D294A63A400263BE5 /* Sources */, + 331C807E294A63A400263BE5 /* Frameworks */, + 331C807F294A63A400263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.demo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.demo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.example.demo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/fastpair/rust/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..e42adcb3 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fastpair/rust/demo/ios/Runner.xcworkspace/contents.xcworkspacedata b/fastpair/rust/demo/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/fastpair/rust/demo/ios/Runner/AppDelegate.swift b/fastpair/rust/demo/ios/Runner/AppDelegate.swift new file mode 100644 index 00000000..70693e4a --- /dev/null +++ b/fastpair/rust/demo/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import Flutter + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 zcmV;526g#~P){YQnis^a@{&-nmRmq)<&%Mztj67_#M}W?l>kYSliK<%xAp;0j{!}J0!o7b zE>q9${Lb$D&h7k=+4=!ek^n+`0zq>LL1O?lVyea53S5x`Nqqo2YyeuIrQrJj9XjOp z{;T5qbj3}&1vg1VK~#9!?b~^C5-}JC@Pyrv-6dSEqJqT}#j9#dJ@GzT@B8}x zU&J@bBI>f6w6en+CeI)3^kC*U?}X%OD8$Fd$H&LV$H&LV$H&LV#|K5~mLYf|VqzOc zkc7qL~0sOYuM{tG`rYEDV{DWY`Z8&)kW*hc2VkBuY+^Yx&92j&StN}Wp=LD zxoGxXw6f&8sB^u})h@b@z0RBeD`K7RMR9deyL(ZJu#39Z>rT)^>v}Khq8U-IbIvT> z?4pV9qGj=2)TNH3d)=De<+^w;>S7m_eFKTvzeaBeir45xY!^m!FmxnljbSS_3o=g( z->^wC9%qkR{kbGnW8MfFew_o9h3(r55Is`L$8KI@d+*%{=Nx+FXJ98L0PjFIu;rGnnfY zn1R5Qnp<{Jq0M1vX=X&F8gtLmcWv$1*M@4ZfF^9``()#hGTeKeP`1!iED ztNE(TN}M5}3Bbc*d=FIv`DNv&@|C6yYj{sSqUj5oo$#*0$7pu|Dd2TLI>t5%I zIa4Dvr(iayb+5x=j*Vum9&irk)xV1`t509lnPO0%skL8_1c#Xbamh(2@f?4yUI zhhuT5<#8RJhGz4%b$`PJwKPAudsm|at?u;*hGgnA zU1;9gnxVBC)wA(BsB`AW54N{|qmikJR*%x0c`{LGsSfa|NK61pYH(r-UQ4_JXd!Rsz)=k zL{GMc5{h138)fF5CzHEDM>+FqY)$pdN3}Ml+riTgJOLN0F*Vh?{9ESR{SVVg>*>=# zix;VJHPtvFFCRY$Ks*F;VX~%*r9F)W`PmPE9F!(&s#x07n2<}?S{(ygpXgX-&B&OM zONY&BRQ(#%0%jeQs?oJ4P!p*R98>qCy5p8w>_gpuh39NcOlp)(wOoz0sY-Qz55eB~ z7OC-fKBaD1sE3$l-6QgBJO!n?QOTza`!S_YK z_v-lm^7{VO^8Q@M_^8F)09Ki6%=s?2_5eupee(w1FB%aqSweusQ-T+CH0Xt{` zFjMvW{@C&TB)k25()nh~_yJ9coBRL(0oO@HK~z}7?bm5j;y@69;bvlHb2tf!$ReA~x{22wTq550 z?f?Hnw(;m3ip30;QzdV~7pi!wyMYhDtXW#cO7T>|f=bdFhu+F!zMZ2UFj;GUKX7tI z;hv3{q~!*pMj75WP_c}>6)IWvg5_yyg<9Op()eD1hWC19M@?_9_MHec{Z8n3FaF{8 z;u`Mw0ly(uE>*CgQYv{be6ab2LWhlaH1^iLIM{olnag$78^Fd}%dR7;JECQ+hmk|o z!u2&!3MqPfP5ChDSkFSH8F2WVOEf0(E_M(JL17G}Y+fg0_IuW%WQ zG(mG&u?|->YSdk0;8rc{yw2@2Z&GA}z{Wb91Ooz9VhA{b2DYE7RmG zjL}?eq#iX%3#k;JWMx_{^2nNax`xPhByFiDX+a7uTGU|otOvIAUy|dEKkXOm-`aWS z27pUzD{a)Ct<6p{{3)+lq@i`t@%>-wT4r?*S}k)58e09WZYP0{{R3FC5Sl00039P)t-s|Ns9~ z#rP?<_5oL$Q^olD{r_0T`27C={r>*`|Nj71npVa5OTzc(_WfbW_({R{p56NV{r*M2 z_xt?)2V0#0NsfV0u>{42ctGP(8vQj-Btk1n|O0ZD=YLwd&R{Ko41Gr9H= zY@z@@bOAMB5Ltl$E>bJJ{>JP30ZxkmI%?eW{k`b?Wy<&gOo;dS`~CR$Vwb@XWtR|N zi~t=w02?-0&j0TD{>bb6sNwsK*!p?V`RMQUl(*DVjk-9Cx+-z1KXab|Ka2oXhX5f% z`$|e!000AhNklrxs)5QTeTVRiEmz~MKK1WAjCw(c-JK6eox;2O)?`? zTG`AHia671e^vgmp!llKp|=5sVHk#C7=~epA~VAf-~%aPC=%Qw01h8mnSZ|p?hz91 z7p83F3%LVu9;S$tSI$C^%^yud1dfTM_6p2|+5Ejp$bd`GDvbR|xit>i!ZD&F>@CJrPmu*UjD&?DfZs=$@e3FQA(vNiU+$A*%a} z?`XcG2jDxJ_ZQ#Md`H{4Lpf6QBDp81_KWZ6Tk#yCy1)32zO#3<7>b`eT7UyYH1eGz z;O(rH$=QR*L%%ZcBpc=eGua?N55nD^K(8<#gl2+pN_j~b2MHs4#mcLmv%DkspS-3< zpI1F=^9siI0s-;IN_IrA;5xm~3?3!StX}pUv0vkxMaqm+zxrg7X7(I&*N~&dEd0kD z-FRV|g=|QuUsuh>-xCI}vD2imzYIOIdcCVV=$Bz@*u0+Bs<|L^)32nN*=wu3n%Ynw z@1|eLG>!8ruU1pFXUfb`j>(=Gy~?Rn4QJ-c3%3T|(Frd!bI`9u&zAnyFYTqlG#&J7 zAkD(jpw|oZLNiA>;>hgp1KX7-wxC~31II47gc zHcehD6Uxlf%+M^^uN5Wc*G%^;>D5qT{>=uxUhX%WJu^Z*(_Wq9y}npFO{Hhb>s6<9 zNi0pHXWFaVZnb)1+RS&F)xOv6&aeILcI)`k#0YE+?e)5&#r7J#c`3Z7x!LpTc01dx zrdC3{Z;joZ^KN&))zB_i)I9fWedoN>Zl-6_Iz+^G&*ak2jpF07*qoM6N<$f;w%0(f|Me literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 00000000..0bedcf2f --- /dev/null +++ b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 00000000..89c2725b --- /dev/null +++ b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/fastpair/rust/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard b/fastpair/rust/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..f2e259c7 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fastpair/rust/demo/ios/Runner/Base.lproj/Main.storyboard b/fastpair/rust/demo/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 00000000..f3c28516 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fastpair/rust/demo/ios/Runner/Info.plist b/fastpair/rust/demo/ios/Runner/Info.plist new file mode 100644 index 00000000..5d784104 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Demo + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + demo + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + + diff --git a/fastpair/rust/demo/ios/Runner/Runner-Bridging-Header.h b/fastpair/rust/demo/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 00000000..308a2a56 --- /dev/null +++ b/fastpair/rust/demo/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/fastpair/rust/demo/ios/RunnerTests/RunnerTests.swift b/fastpair/rust/demo/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..86a7c3b1 --- /dev/null +++ b/fastpair/rust/demo/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/fastpair/rust/demo/lib/main.dart b/fastpair/rust/demo/lib/main.dart new file mode 100644 index 00000000..dda55548 --- /dev/null +++ b/fastpair/rust/demo/lib/main.dart @@ -0,0 +1,125 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + // This widget is the root of your application. + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + // This is the theme of your application. + // + // TRY THIS: Try running your application with "flutter run". You'll see + // the application has a blue toolbar. Then, without quitting the app, + // try changing the seedColor in the colorScheme below to Colors.green + // and then invoke "hot reload" (save your changes or press the "hot + // reload" button in a Flutter-supported IDE, or press "r" if you used + // the command line to start the app). + // + // Notice that the counter didn't reset back to zero; the application + // state is not lost during the reload. To reset the state, use hot + // restart instead. + // + // This works for code too, not just values: Most code changes can be + // tested with just a hot reload. + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const MyHomePage(title: 'Flutter Demo Home Page'), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key, required this.title}); + + // This widget is the home page of your application. It is stateful, meaning + // that it has a State object (defined below) that contains fields that affect + // how it looks. + + // This class is the configuration for the state. It holds the values (in this + // case the title) provided by the parent (in this case the App widget) and + // used by the build method of the State. Fields in a Widget subclass are + // always marked "final". + + final String title; + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + int _counter = 0; + + void _incrementCounter() { + setState(() { + // This call to setState tells the Flutter framework that something has + // changed in this State, which causes it to rerun the build method below + // so that the display can reflect the updated values. If we changed + // _counter without calling setState(), then the build method would not be + // called again, and so nothing would appear to happen. + _counter++; + }); + } + + @override + Widget build(BuildContext context) { + // This method is rerun every time setState is called, for instance as done + // by the _incrementCounter method above. + // + // The Flutter framework has been optimized to make rerunning build methods + // fast, so that you can just rebuild anything that needs updating rather + // than having to individually change instances of widgets. + return Scaffold( + appBar: AppBar( + // TRY THIS: Try changing the color here to a specific color (to + // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar + // change color while the other colors stay the same. + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: Text(widget.title), + ), + body: Center( + // Center is a layout widget. It takes a single child and positions it + // in the middle of the parent. + child: Column( + // Column is also a layout widget. It takes a list of children and + // arranges them vertically. By default, it sizes itself to fit its + // children horizontally, and tries to be as tall as its parent. + // + // Column has various properties to control how it sizes itself and + // how it positions its children. Here we use mainAxisAlignment to + // center the children vertically; the main axis here is the vertical + // axis because Columns are vertical (the cross axis would be + // horizontal). + // + // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" + // action in the IDE, or press "p" in the console), to see the + // wireframe for each widget. + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'You have pushed the button this many times:', + ), + Text( + '$_counter', + style: Theme.of(context).textTheme.headlineMedium, + ), + ], + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _incrementCounter, + tooltip: 'Increment', + child: const Icon(Icons.add), + ), // This trailing comma makes auto-formatting nicer for build methods. + ); + } +} diff --git a/fastpair/rust/demo/linux/.gitignore b/fastpair/rust/demo/linux/.gitignore new file mode 100644 index 00000000..d3896c98 --- /dev/null +++ b/fastpair/rust/demo/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/fastpair/rust/demo/linux/CMakeLists.txt b/fastpair/rust/demo/linux/CMakeLists.txt new file mode 100644 index 00000000..d8d150ac --- /dev/null +++ b/fastpair/rust/demo/linux/CMakeLists.txt @@ -0,0 +1,139 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "demo") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.demo") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/fastpair/rust/demo/linux/flutter/CMakeLists.txt b/fastpair/rust/demo/linux/flutter/CMakeLists.txt new file mode 100644 index 00000000..d5bd0164 --- /dev/null +++ b/fastpair/rust/demo/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.cc b/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..e71a16d2 --- /dev/null +++ b/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.h b/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..e0f0a47b --- /dev/null +++ b/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/fastpair/rust/demo/linux/flutter/generated_plugins.cmake b/fastpair/rust/demo/linux/flutter/generated_plugins.cmake new file mode 100644 index 00000000..2e1de87a --- /dev/null +++ b/fastpair/rust/demo/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/fastpair/rust/demo/linux/main.cc b/fastpair/rust/demo/linux/main.cc new file mode 100644 index 00000000..e7c5c543 --- /dev/null +++ b/fastpair/rust/demo/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/fastpair/rust/demo/linux/my_application.cc b/fastpair/rust/demo/linux/my_application.cc new file mode 100644 index 00000000..0d6f1cce --- /dev/null +++ b/fastpair/rust/demo/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "demo"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "demo"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/fastpair/rust/demo/linux/my_application.h b/fastpair/rust/demo/linux/my_application.h new file mode 100644 index 00000000..72271d5e --- /dev/null +++ b/fastpair/rust/demo/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/fastpair/rust/demo/macos/.gitignore b/fastpair/rust/demo/macos/.gitignore new file mode 100644 index 00000000..746adbb6 --- /dev/null +++ b/fastpair/rust/demo/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/fastpair/rust/demo/macos/Flutter/Flutter-Debug.xcconfig b/fastpair/rust/demo/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 00000000..c2efd0b6 --- /dev/null +++ b/fastpair/rust/demo/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/fastpair/rust/demo/macos/Flutter/Flutter-Release.xcconfig b/fastpair/rust/demo/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 00000000..c2efd0b6 --- /dev/null +++ b/fastpair/rust/demo/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1 @@ +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/fastpair/rust/demo/macos/Flutter/GeneratedPluginRegistrant.swift b/fastpair/rust/demo/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 00000000..cccf817a --- /dev/null +++ b/fastpair/rust/demo/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,10 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { +} diff --git a/fastpair/rust/demo/macos/Runner.xcodeproj/project.pbxproj b/fastpair/rust/demo/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 00000000..de4d6636 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,695 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* demo.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* demo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1300; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/demo"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/demo"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/demo"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/fastpair/rust/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/fastpair/rust/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/fastpair/rust/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/fastpair/rust/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 00000000..0b1d244f --- /dev/null +++ b/fastpair/rust/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fastpair/rust/demo/macos/Runner.xcworkspace/contents.xcworkspacedata b/fastpair/rust/demo/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..1d526a16 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/fastpair/rust/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/fastpair/rust/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/fastpair/rust/demo/macos/Runner/AppDelegate.swift b/fastpair/rust/demo/macos/Runner/AppDelegate.swift new file mode 100644 index 00000000..d53ef643 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..a2ec33f1 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fastpair/rust/demo/macos/Runner/Configs/AppInfo.xcconfig b/fastpair/rust/demo/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 00000000..6ad99cad --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = demo + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.demo + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. diff --git a/fastpair/rust/demo/macos/Runner/Configs/Debug.xcconfig b/fastpair/rust/demo/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 00000000..36b0fd94 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/fastpair/rust/demo/macos/Runner/Configs/Release.xcconfig b/fastpair/rust/demo/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 00000000..dff4f495 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/fastpair/rust/demo/macos/Runner/Configs/Warnings.xcconfig b/fastpair/rust/demo/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 00000000..42bcbf47 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/fastpair/rust/demo/macos/Runner/DebugProfile.entitlements b/fastpair/rust/demo/macos/Runner/DebugProfile.entitlements new file mode 100644 index 00000000..dddb8a30 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + + diff --git a/fastpair/rust/demo/macos/Runner/Info.plist b/fastpair/rust/demo/macos/Runner/Info.plist new file mode 100644 index 00000000..4789daa6 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/fastpair/rust/demo/macos/Runner/MainFlutterWindow.swift b/fastpair/rust/demo/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 00000000..3cc05eb2 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/fastpair/rust/demo/macos/Runner/Release.entitlements b/fastpair/rust/demo/macos/Runner/Release.entitlements new file mode 100644 index 00000000..852fa1a4 --- /dev/null +++ b/fastpair/rust/demo/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.app-sandbox + + + diff --git a/fastpair/rust/demo/macos/RunnerTests/RunnerTests.swift b/fastpair/rust/demo/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 00000000..5418c9f5 --- /dev/null +++ b/fastpair/rust/demo/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import FlutterMacOS +import Cocoa +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/fastpair/rust/demo/pubspec.lock b/fastpair/rust/demo/pubspec.lock new file mode 100644 index 00000000..a6349a4e --- /dev/null +++ b/fastpair/rust/demo/pubspec.lock @@ -0,0 +1,188 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + collection: + dependency: transitive + description: + name: collection + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + url: "https://pub.dev" + source: hosted + version: "1.17.1" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: e35129dc44c9118cee2a5603506d823bab99c68393879edb440e0090d07586be + url: "https://pub.dev" + source: hosted + version: "1.0.5" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "2118df84ef0c3ca93f96123a616ae8540879991b8b57af2f81b76a7ada49b2a4" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + url: "https://pub.dev" + source: hosted + version: "0.12.15" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + meta: + dependency: transitive + description: + name: meta + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path: + dependency: transitive + description: + name: path + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + url: "https://pub.dev" + source: hosted + version: "1.8.3" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_span: + dependency: transitive + description: + name: source_span + sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + url: "https://pub.dev" + source: hosted + version: "1.9.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + url: "https://pub.dev" + source: hosted + version: "0.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" +sdks: + dart: ">=3.0.6 <4.0.0" diff --git a/fastpair/rust/demo/pubspec.yaml b/fastpair/rust/demo/pubspec.yaml new file mode 100644 index 00000000..d1ef1a3d --- /dev/null +++ b/fastpair/rust/demo/pubspec.yaml @@ -0,0 +1,90 @@ +name: demo +description: FLutter UI demo for Fast Pair Windows written in Rust. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: '>=3.0.6 <4.0.0' + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/fastpair/rust/demo/test/widget_test.dart b/fastpair/rust/demo/test/widget_test.dart new file mode 100644 index 00000000..e25abebe --- /dev/null +++ b/fastpair/rust/demo/test/widget_test.dart @@ -0,0 +1,30 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:demo/main.dart'; + +void main() { + testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // Build our app and trigger a frame. + await tester.pumpWidget(const MyApp()); + + // Verify that our counter starts at 0. + expect(find.text('0'), findsOneWidget); + expect(find.text('1'), findsNothing); + + // Tap the '+' icon and trigger a frame. + await tester.tap(find.byIcon(Icons.add)); + await tester.pump(); + + // Verify that our counter has incremented. + expect(find.text('0'), findsNothing); + expect(find.text('1'), findsOneWidget); + }); +} diff --git a/fastpair/rust/demo/web/favicon.png b/fastpair/rust/demo/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/web/icons/Icon-192.png b/fastpair/rust/demo/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/web/icons/Icon-512.png b/fastpair/rust/demo/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/web/icons/Icon-maskable-192.png b/fastpair/rust/demo/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/web/icons/Icon-maskable-512.png b/fastpair/rust/demo/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/web/index.html b/fastpair/rust/demo/web/index.html new file mode 100644 index 00000000..1a98da7d --- /dev/null +++ b/fastpair/rust/demo/web/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + demo + + + + + + + + + + diff --git a/fastpair/rust/demo/web/manifest.json b/fastpair/rust/demo/web/manifest.json new file mode 100644 index 00000000..238a284b --- /dev/null +++ b/fastpair/rust/demo/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "demo", + "short_name": "demo", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/fastpair/rust/demo/windows/.gitignore b/fastpair/rust/demo/windows/.gitignore new file mode 100644 index 00000000..d492d0d9 --- /dev/null +++ b/fastpair/rust/demo/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/fastpair/rust/demo/windows/CMakeLists.txt b/fastpair/rust/demo/windows/CMakeLists.txt new file mode 100644 index 00000000..15d0aae2 --- /dev/null +++ b/fastpair/rust/demo/windows/CMakeLists.txt @@ -0,0 +1,102 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(demo LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "demo") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/fastpair/rust/demo/windows/flutter/CMakeLists.txt b/fastpair/rust/demo/windows/flutter/CMakeLists.txt new file mode 100644 index 00000000..930d2071 --- /dev/null +++ b/fastpair/rust/demo/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 00000000..8b6d4680 --- /dev/null +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void RegisterPlugins(flutter::PluginRegistry* registry) { +} diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 00000000..dc139d85 --- /dev/null +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/fastpair/rust/demo/windows/flutter/generated_plugins.cmake b/fastpair/rust/demo/windows/flutter/generated_plugins.cmake new file mode 100644 index 00000000..b93c4c30 --- /dev/null +++ b/fastpair/rust/demo/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/fastpair/rust/demo/windows/runner/CMakeLists.txt b/fastpair/rust/demo/windows/runner/CMakeLists.txt new file mode 100644 index 00000000..394917c0 --- /dev/null +++ b/fastpair/rust/demo/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/fastpair/rust/demo/windows/runner/Runner.rc b/fastpair/rust/demo/windows/runner/Runner.rc new file mode 100644 index 00000000..3c7cdc73 --- /dev/null +++ b/fastpair/rust/demo/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.example" "\0" + VALUE "FileDescription", "demo" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "demo" "\0" + VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "demo.exe" "\0" + VALUE "ProductName", "demo" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/fastpair/rust/demo/windows/runner/flutter_window.cpp b/fastpair/rust/demo/windows/runner/flutter_window.cpp new file mode 100644 index 00000000..b25e363e --- /dev/null +++ b/fastpair/rust/demo/windows/runner/flutter_window.cpp @@ -0,0 +1,66 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/fastpair/rust/demo/windows/runner/flutter_window.h b/fastpair/rust/demo/windows/runner/flutter_window.h new file mode 100644 index 00000000..6da0652f --- /dev/null +++ b/fastpair/rust/demo/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/fastpair/rust/demo/windows/runner/main.cpp b/fastpair/rust/demo/windows/runner/main.cpp new file mode 100644 index 00000000..fec4dbaa --- /dev/null +++ b/fastpair/rust/demo/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"demo", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/fastpair/rust/demo/windows/runner/resource.h b/fastpair/rust/demo/windows/runner/resource.h new file mode 100644 index 00000000..66a65d1e --- /dev/null +++ b/fastpair/rust/demo/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/fastpair/rust/demo/windows/runner/resources/app_icon.ico b/fastpair/rust/demo/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/fastpair/rust/demo/windows/runner/runner.exe.manifest b/fastpair/rust/demo/windows/runner/runner.exe.manifest new file mode 100644 index 00000000..a42ea768 --- /dev/null +++ b/fastpair/rust/demo/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/fastpair/rust/demo/windows/runner/utils.cpp b/fastpair/rust/demo/windows/runner/utils.cpp new file mode 100644 index 00000000..b2b08734 --- /dev/null +++ b/fastpair/rust/demo/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length <= 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/fastpair/rust/demo/windows/runner/utils.h b/fastpair/rust/demo/windows/runner/utils.h new file mode 100644 index 00000000..3879d547 --- /dev/null +++ b/fastpair/rust/demo/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/fastpair/rust/demo/windows/runner/win32_window.cpp b/fastpair/rust/demo/windows/runner/win32_window.cpp new file mode 100644 index 00000000..60608d0f --- /dev/null +++ b/fastpair/rust/demo/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/fastpair/rust/demo/windows/runner/win32_window.h b/fastpair/rust/demo/windows/runner/win32_window.h new file mode 100644 index 00000000..e901dde6 --- /dev/null +++ b/fastpair/rust/demo/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ From d7aedea8150f0a80e034c686afd3a707354f48c3 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sat, 29 Jul 2023 15:49:19 -0700 Subject: [PATCH 055/128] [fp-rs] Initial Rust dummy commit for Flutter UI demo --- fastpair/rust/demo/rust/Cargo.toml | 12 ++++++++++++ fastpair/rust/demo/rust/src/api.rs | 3 +++ fastpair/rust/demo/rust/src/lib.rs | 1 + 3 files changed, 16 insertions(+) create mode 100644 fastpair/rust/demo/rust/Cargo.toml create mode 100644 fastpair/rust/demo/rust/src/api.rs create mode 100644 fastpair/rust/demo/rust/src/lib.rs diff --git a/fastpair/rust/demo/rust/Cargo.toml b/fastpair/rust/demo/rust/Cargo.toml new file mode 100644 index 00000000..41d5928c --- /dev/null +++ b/fastpair/rust/demo/rust/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +crate-type = ["lib", "cdylib", "staticlib"] + +[dependencies] +flutter_rust_bridge = "1" diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs new file mode 100644 index 00000000..812479a2 --- /dev/null +++ b/fastpair/rust/demo/rust/src/api.rs @@ -0,0 +1,3 @@ +pub fn hello() -> String { + String::from("Rust says hi!") +} diff --git a/fastpair/rust/demo/rust/src/lib.rs b/fastpair/rust/demo/rust/src/lib.rs new file mode 100644 index 00000000..b32f9e29 --- /dev/null +++ b/fastpair/rust/demo/rust/src/lib.rs @@ -0,0 +1 @@ +mod api; From 80a3b508fa9b518a464431186eb5e2daeaff1c16 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sat, 29 Jul 2023 16:02:15 -0700 Subject: [PATCH 056/128] [fp-rs] Providing constructs for generating Rust-side bridge code --- fastpair/rust/demo/bindgen.script | 1 + fastpair/rust/demo/pubspec.lock | 456 ++++++++++++++++++ fastpair/rust/demo/pubspec.yaml | 6 + .../rust/demo/rust/src/bridge_generated.io.rs | 36 ++ .../rust/demo/rust/src/bridge_generated.rs | 69 +++ fastpair/rust/demo/rust/src/lib.rs | 1 + fastpair/rust/demo/windows/CMakeLists.txt | 1 + fastpair/rust/demo/windows/rust.cmake | 21 + 8 files changed, 591 insertions(+) create mode 100644 fastpair/rust/demo/bindgen.script create mode 100644 fastpair/rust/demo/rust/src/bridge_generated.io.rs create mode 100644 fastpair/rust/demo/rust/src/bridge_generated.rs create mode 100644 fastpair/rust/demo/windows/rust.cmake diff --git a/fastpair/rust/demo/bindgen.script b/fastpair/rust/demo/bindgen.script new file mode 100644 index 00000000..b8c3eb48 --- /dev/null +++ b/fastpair/rust/demo/bindgen.script @@ -0,0 +1 @@ +flutter_rust_bridge_codegen --rust-input rust/src/api.rs --dart-output lib/bridge_generated.dart --dart-decl-output lib/bridge_definitions.dart diff --git a/fastpair/rust/demo/pubspec.lock b/fastpair/rust/demo/pubspec.lock index a6349a4e..a6087902 100644 --- a/fastpair/rust/demo/pubspec.lock +++ b/fastpair/rust/demo/pubspec.lock @@ -1,6 +1,38 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.dev" + source: hosted + version: "61.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.dev" + source: hosted + version: "5.13.0" + archive: + dependency: transitive + description: + name: archive + sha256: "0c8368c9b3f0abbc193b9d6133649a614204b528982bebc7026372d61677ce3a" + url: "https://pub.dev" + source: hosted + version: "3.3.7" + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" async: dependency: transitive description: @@ -17,6 +49,78 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_cli_annotations: + dependency: transitive + description: + name: build_cli_annotations + sha256: b59d2769769efd6c9ff6d4c4cede0be115a566afc591705c2040b707534b1172 + url: "https://pub.dev" + source: hosted + version: "2.1.0" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "5f02d73eb2ba16483e693f80bee4f088563a820e47d1027d4cdfe62b5bb43e65" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "6c4dd11d05d056e76320b828a1db0fc01ccd376922526f8e9d6c796a5adbac20" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "10c6bcdbf9d049a0b666702cf1cee4ddfdc38f02a19d35ae392863b47519848b" + url: "https://pub.dev" + source: hosted + version: "2.4.6" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41" + url: "https://pub.dev" + source: hosted + version: "7.2.10" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166" + url: "https://pub.dev" + source: hosted + version: "8.6.1" characters: dependency: transitive description: @@ -25,6 +129,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: b8db3080e59b2503ca9e7922c3df2072cf13992354d5e944074ffa836fba43b7 + url: "https://pub.dev" + source: hosted + version: "0.4.0" clock: dependency: transitive description: @@ -33,6 +153,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189" + url: "https://pub.dev" + source: hosted + version: "4.5.0" collection: dependency: transitive description: @@ -41,6 +169,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.17.1" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" cupertino_icons: dependency: "direct main" description: @@ -49,6 +193,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.5" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + url: "https://pub.dev" + source: hosted + version: "2.3.2" fake_async: dependency: transitive description: @@ -57,6 +209,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: "direct main" + description: + name: ffi + sha256: ed5337a5660c506388a9f012be0288fb38b49020ce2b45fe1f8b8323fe429f99 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + ffigen: + dependency: "direct dev" + description: + name: ffigen + sha256: d3e76c2ad48a4e7f93a29a162006f00eba46ce7c08194a77bb5c5e97d1b5ff0a + url: "https://pub.dev" + source: hosted + version: "8.0.2" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" flutter: dependency: "direct main" description: flutter @@ -70,11 +254,91 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + flutter_rust_bridge: + dependency: "direct main" + description: + name: flutter_rust_bridge + sha256: dcb436ba4b466e19da1656ef14622b5ac2ed90efc8fcb0946942c6cd185578d1 + url: "https://pub.dev" + source: hosted + version: "1.79.0" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: "2df89855fe181baae3b6d714dc3c4317acf4fccd495a6f36e5e00f24144c6c3b" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + url: "https://pub.dev" + source: hosted + version: "2.4.1" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + http: + dependency: transitive + description: + name: http + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" js: dependency: transitive description: @@ -83,6 +347,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" lints: dependency: transitive description: @@ -91,6 +363,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: @@ -115,6 +395,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + mime: + dependency: transitive + description: + name: mime + sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + url: "https://pub.dev" + source: hosted + version: "1.0.4" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: @@ -123,11 +419,99 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.3" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 + url: "https://pub.dev" + source: hosted + version: "5.4.0" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c" + url: "https://pub.dev" + source: hosted + version: "3.7.3" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + puppeteer: + dependency: transitive + description: + name: puppeteer + sha256: f00b54703dc22af04eaace8f23a33c56008870f990684c2ad8c4115ac51b0a38 + url: "https://pub.dev" + source: hosted + version: "3.1.1" + quiver: + dependency: transitive + description: + name: quiver + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + url: "https://pub.dev" + source: hosted + version: "3.2.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e + url: "https://pub.dev" + source: hosted + version: "1.1.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: fc0da689e5302edb6177fdd964efcb7f58912f43c28c2047a808f5bfff643d16 + url: "https://pub.dev" + source: hosted + version: "1.4.0" source_span: dependency: transitive description: @@ -152,6 +536,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" string_scanner: dependency: transitive description: @@ -176,6 +568,38 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + uuid: + dependency: transitive + description: + name: uuid + sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313" + url: "https://pub.dev" + source: hosted + version: "3.0.7" vector_math: dependency: transitive description: @@ -184,5 +608,37 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + url: "https://pub.dev" + source: hosted + version: "2.4.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: "1579d4a0340a83cf9e4d580ea51a16329c916973bffd5bd4b45e911b25d46bfd" + url: "https://pub.dev" + source: hosted + version: "2.1.1" sdks: dart: ">=3.0.6 <4.0.0" diff --git a/fastpair/rust/demo/pubspec.yaml b/fastpair/rust/demo/pubspec.yaml index d1ef1a3d..3ae724d4 100644 --- a/fastpair/rust/demo/pubspec.yaml +++ b/fastpair/rust/demo/pubspec.yaml @@ -35,6 +35,9 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 + ffi: ^2.0.2 + flutter_rust_bridge: ^1.79.0 + freezed_annotation: ^2.4.1 dev_dependencies: flutter_test: @@ -46,6 +49,9 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^2.0.0 + ffigen: ^8.0.2 + build_runner: ^2.4.6 + freezed: ^2.4.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/fastpair/rust/demo/rust/src/bridge_generated.io.rs b/fastpair/rust/demo/rust/src/bridge_generated.io.rs new file mode 100644 index 00000000..5bc455d3 --- /dev/null +++ b/fastpair/rust/demo/rust/src/bridge_generated.io.rs @@ -0,0 +1,36 @@ +use super::*; +// Section: wire functions + +#[no_mangle] +pub extern "C" fn wire_hello(port_: i64) { + wire_hello_impl(port_) +} + +// Section: allocate functions + +// Section: related functions + +// Section: impl Wire2Api + +// Section: wire structs + +// Section: impl NewWithNullPtr + +pub trait NewWithNullPtr { + fn new_with_null_ptr() -> Self; +} + +impl NewWithNullPtr for *mut T { + fn new_with_null_ptr() -> Self { + std::ptr::null_mut() + } +} + +// Section: sync execution mode utility + +#[no_mangle] +pub extern "C" fn free_WireSyncReturn(ptr: support::WireSyncReturn) { + unsafe { + let _ = support::box_from_leak_ptr(ptr); + }; +} diff --git a/fastpair/rust/demo/rust/src/bridge_generated.rs b/fastpair/rust/demo/rust/src/bridge_generated.rs new file mode 100644 index 00000000..710e8db5 --- /dev/null +++ b/fastpair/rust/demo/rust/src/bridge_generated.rs @@ -0,0 +1,69 @@ +#![allow( + non_camel_case_types, + unused, + clippy::redundant_closure, + clippy::useless_conversion, + clippy::unit_arg, + clippy::double_parens, + non_snake_case, + clippy::too_many_arguments +)] +// AUTO GENERATED FILE, DO NOT EDIT. +// Generated by `flutter_rust_bridge`@ 1.79.0. + +use crate::api::*; +use core::panic::UnwindSafe; +use flutter_rust_bridge::rust2dart::IntoIntoDart; +use flutter_rust_bridge::*; +use std::ffi::c_void; +use std::sync::Arc; + +// Section: imports + +// Section: wire functions + +fn wire_hello_impl(port_: MessagePort) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, String>( + WrapInfo { + debug_name: "hello", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || move |task_callback| Ok(hello()), + ) +} +// Section: wrapper structs + +// Section: static checks + +// Section: allocate functions + +// Section: related functions + +// Section: impl Wire2Api + +pub trait Wire2Api { + fn wire2api(self) -> T; +} + +impl Wire2Api> for *mut S +where + *mut S: Wire2Api, +{ + fn wire2api(self) -> Option { + (!self.is_null()).then(|| self.wire2api()) + } +} +// Section: impl IntoDart + +// Section: executor + +support::lazy_static! { + pub static ref FLUTTER_RUST_BRIDGE_HANDLER: support::DefaultHandler = Default::default(); +} + +#[cfg(not(target_family = "wasm"))] +#[path = "bridge_generated.io.rs"] +mod io; +#[cfg(not(target_family = "wasm"))] +pub use io::*; diff --git a/fastpair/rust/demo/rust/src/lib.rs b/fastpair/rust/demo/rust/src/lib.rs index b32f9e29..97e8ba25 100644 --- a/fastpair/rust/demo/rust/src/lib.rs +++ b/fastpair/rust/demo/rust/src/lib.rs @@ -1 +1,2 @@ +mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ mod api; diff --git a/fastpair/rust/demo/windows/CMakeLists.txt b/fastpair/rust/demo/windows/CMakeLists.txt index 15d0aae2..172a9862 100644 --- a/fastpair/rust/demo/windows/CMakeLists.txt +++ b/fastpair/rust/demo/windows/CMakeLists.txt @@ -56,6 +56,7 @@ add_subdirectory("runner") # Generated plugin build rules, which manage building the plugins and adding # them to the application. include(flutter/generated_plugins.cmake) +include(./rust.cmake) # === Installation === diff --git a/fastpair/rust/demo/windows/rust.cmake b/fastpair/rust/demo/windows/rust.cmake new file mode 100644 index 00000000..4931b359 --- /dev/null +++ b/fastpair/rust/demo/windows/rust.cmake @@ -0,0 +1,21 @@ +# We include Corrosion inline here, but ideally in a project with +# many dependencies we would need to install Corrosion on the system. +# See instructions on https://github.com/AndrewGaspar/corrosion#cmake-install +# Once done, uncomment this line: +# find_package(Corrosion REQUIRED) + +include(FetchContent) + +FetchContent_Declare( + Corrosion + GIT_REPOSITORY https://github.com/AndrewGaspar/corrosion.git + GIT_TAG origin/master # Optionally specify a version tag or branch here +) + +FetchContent_MakeAvailable(Corrosion) + +corrosion_import_crate(MANIFEST_PATH ../rust/Cargo.toml IMPORTED_CRATES imported_crates) +target_link_libraries(${BINARY_NAME} PRIVATE ${imported_crates}) +foreach(imported_crate ${imported_crates}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) +endforeach() \ No newline at end of file From b6459434a209b5b029e160faa299284f145e2043 Mon Sep 17 00:00:00 2001 From: hai007 Date: Wed, 2 Aug 2023 21:04:41 +0000 Subject: [PATCH 057/128] Update ranging interval from update rates. PiperOrigin-RevId: 553250662 --- presence/proto/presence_frame.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/presence/proto/presence_frame.proto b/presence/proto/presence_frame.proto index 62fd7d47..57d66071 100644 --- a/presence/proto/presence_frame.proto +++ b/presence/proto/presence_frame.proto @@ -117,7 +117,7 @@ message UwbControleeCapabilities { optional bool is_ranging_interval_reconfigure_supported = 14 [default = false]; repeated int32 supported_slot_durations = 15 [packed = true]; - repeated int32 supported_ranging_intervals = 16 [packed = true]; + repeated int32 supported_ranging_update_rates = 16 [packed = true]; } /** From 886ac8a2b9a46f7dc6aa657ae2ddbcbfb626d57d Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Wed, 2 Aug 2023 15:13:58 -0700 Subject: [PATCH 058/128] Remove device from local repository when user forget a device PiperOrigin-RevId: 553269983 --- fastpair/internal/BUILD | 7 +- fastpair/internal/fast_pair_seeker_impl.cc | 20 +++++ fastpair/internal/fast_pair_seeker_impl.h | 2 + .../internal/fast_pair_seeker_impl_test.cc | 79 +++++++++++++++++++ fastpair/repository/BUILD | 2 + .../repository/fake_fast_pair_repository.cc | 23 ++++++ .../repository/fake_fast_pair_repository.h | 8 +- .../repository/fast_pair_device_repository.cc | 18 ++++- .../repository/fast_pair_device_repository.h | 3 + .../fast_pair_device_repository_test.cc | 23 +++++- 10 files changed, 178 insertions(+), 7 deletions(-) diff --git a/fastpair/internal/BUILD b/fastpair/internal/BUILD index cbec33b4..3742f983 100644 --- a/fastpair/internal/BUILD +++ b/fastpair/internal/BUILD @@ -14,6 +14,7 @@ cc_library( "//fastpair:fast_pair_controller", "//fastpair:fast_pair_events", "//fastpair:fast_pair_seeker", + "//fastpair/common", "//fastpair/internal/mediums", "//fastpair/pairing", "//fastpair/repository", @@ -39,6 +40,9 @@ cc_test( "//fastpair/common", "//fastpair/message_stream:fake_gatt_callbacks", "//fastpair/message_stream:fake_provider", + "//fastpair/proto:fastpair_cc_proto", + "//fastpair/repository", + "//fastpair/repository:device_repository", "//fastpair/repository:test_support", "//internal/account:test_support", "//internal/platform:test_util", @@ -46,7 +50,8 @@ cc_test( "//internal/platform/implementation/g3", # build_cleaner: keep "//internal/test/google3_only:test", "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/time", + "@com_google_absl//absl/status", + "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_main", ], ) diff --git a/fastpair/internal/fast_pair_seeker_impl.cc b/fastpair/internal/fast_pair_seeker_impl.cc index 10f7bafe..30b27f95 100644 --- a/fastpair/internal/fast_pair_seeker_impl.cc +++ b/fastpair/internal/fast_pair_seeker_impl.cc @@ -21,11 +21,13 @@ #include "absl/status/status.h" #include "absl/strings/str_format.h" +#include "fastpair/common/account_key.h" #include "fastpair/fast_pair_controller.h" #include "fastpair/fast_pair_events.h" #include "fastpair/pairing/pairer_broker_impl.h" #include "fastpair/scanning/scanner_broker_impl.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/logging.h" #include "internal/platform/pending_job_registry.h" #include "internal/platform/single_thread_executor.h" @@ -303,5 +305,23 @@ void FastPairSeekerImpl::OnRetroactivePairFound(FastPairDevice& device) { callbacks_.on_pair_event(device, PairEvent{.is_paired = true}); } +void FastPairSeekerImpl::ForgetDeviceByAccountKey( + const AccountKey& account_key) { + NEARBY_LOGS(VERBOSE) << __func__; + auto opt_device = devices_->FindDevice(account_key); + if (!opt_device.has_value()) { + NEARBY_LOGS(INFO) << __func__ << "No FP device matching the account key."; + } else { + devices_->RemoveDevice(opt_device.value()); + } + + repository_->DeleteAssociatedDeviceByAccountKey( + account_key, [&](absl::Status success) { + if (!success.ok()) return; + NEARBY_LOGS(VERBOSE) << "Deleted associated devcie by account key"; + // Temporary solution to refresh the saved_devices_sheet. + repository_->GetUserSavedDevices(); + }); +} } // namespace fastpair } // namespace nearby diff --git a/fastpair/internal/fast_pair_seeker_impl.h b/fastpair/internal/fast_pair_seeker_impl.h index 8984b22e..bf096f2b 100644 --- a/fastpair/internal/fast_pair_seeker_impl.h +++ b/fastpair/internal/fast_pair_seeker_impl.h @@ -43,6 +43,7 @@ class FastPairSeekerExt : public FastPairSeeker { // Handle the state changes of screen lock. virtual void SetIsScreenLocked(bool is_locked) = 0; + virtual void ForgetDeviceByAccountKey(const AccountKey& account_key) = 0; }; class FastPairSeekerImpl : public FastPairSeekerExt, @@ -91,6 +92,7 @@ class FastPairSeekerImpl : public FastPairSeekerExt, absl::Status StartFastPairScan() override; absl::Status StopFastPairScan() override; void SetIsScreenLocked(bool is_locked) override; + void ForgetDeviceByAccountKey(const AccountKey& account_key) override; // From BluetoothClassicMedium::Observer. void DeviceAdded(BluetoothDevice& device) override; diff --git a/fastpair/internal/fast_pair_seeker_impl_test.cc b/fastpair/internal/fast_pair_seeker_impl_test.cc index 9957265c..09556b8e 100644 --- a/fastpair/internal/fast_pair_seeker_impl_test.cc +++ b/fastpair/internal/fast_pair_seeker_impl_test.cc @@ -18,18 +18,27 @@ #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/escaping.h" +#include "fastpair/common/fast_pair_device.h" #include "fastpair/common/fast_pair_prefs.h" #include "fastpair/fast_pair_events.h" #include "fastpair/fast_pair_seeker.h" #include "fastpair/message_stream/fake_gatt_callbacks.h" #include "fastpair/message_stream/fake_provider.h" +#include "fastpair/proto/data.proto.h" +#include "fastpair/proto/enum.proto.h" #include "fastpair/repository/fake_fast_pair_repository.h" +#include "fastpair/repository/fast_pair_device_repository.h" +#include "fastpair/repository/fast_pair_repository.h" #include "internal/account/fake_account_manager.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" #include "internal/platform/single_thread_executor.h" #include "internal/platform/task_runner_impl.h" @@ -61,6 +70,19 @@ class MediumEnvironmentStarter { ~MediumEnvironmentStarter() { MediumEnvironment::Instance().Stop(); } }; +class FastPairRepositoryObserver : public FastPairRepository::Observer { + public: + explicit FastPairRepositoryObserver(CountDownLatch* latch) { latch_ = latch; } + + void OnGetUserSavedDevices( + const proto::OptInStatus& opt_in_status, + const std::vector& devices) override { + latch_->CountDown(); + } + + CountDownLatch* latch_ = nullptr; +}; + class FastPairSeekerImplTest : public testing::Test { protected: FastPairSeekerImplTest() { @@ -222,6 +244,63 @@ TEST_F(FastPairSeekerImplTest, InitialPairing) { fast_pair_seeker_.reset(); } +TEST_F(FastPairSeekerImplTest, ForgetDeviceByAccountKey) { + NEARBY_LOG_SET_SEVERITY(VERBOSE); + FakeProvider provider; + CountDownLatch discover_latch(1); + CountDownLatch pair_latch(1); + fast_pair_seeker_ = std::make_unique( + FastPairSeekerImpl::ServiceCallbacks{ + .on_initial_discovery = + [&](const FastPairDevice& device, InitialDiscoveryEvent event) { + EXPECT_EQ(device.GetModelId(), kModelId); + EXPECT_OK(fast_pair_seeker_->StartInitialPairing( + device, {}, + {.on_pairing_result = [&](const FastPairDevice& device, + absl::Status status) { + EXPECT_EQ(device.GetBleAddress(), + provider.GetMacAddress()); + EXPECT_OK(status); + pair_latch.CountDown(); + }})); + discover_latch.CountDown(); + }}, + &executor_, account_manager_.get(), &devices_, repository_.get()); + + EXPECT_OK(fast_pair_seeker_->StartFastPairScan()); + provider.PrepareForInitialPairing( + { + .private_key = absl::HexStringToBytes(kBobPrivateKey), + .public_key = absl::HexStringToBytes(kBobPublicKey), + .model_id = std::string(kModelId), + .pass_key = std::string(kPasskey), + }, + &fake_gatt_callbacks_); + + discover_latch.Await(); + pair_latch.Await(); + auto fp_device = devices_.FindDevice(provider.GetMacAddress()); + ASSERT_TRUE(fp_device.has_value()); + EXPECT_EQ(provider.GetAccountKey(), fp_device.value()->GetAccountKey()); + + // Adds FastPairRepository observer. + CountDownLatch repository_latch(1); + FastPairRepositoryObserver observer(&repository_latch); + repository_->AddObserver(&observer); + // Adds FastPairDeviceRepository observer. + CountDownLatch devices_latch(1); + FastPairDeviceRepository::RemoveDeviceCallback callback = + [&](const FastPairDevice& device) { devices_latch.CountDown(); }; + devices_.AddObserver(&callback); + + fast_pair_seeker_->ForgetDeviceByAccountKey( + fp_device.value()->GetAccountKey()); + repository_latch.Await(); + devices_latch.Await(); + EXPECT_FALSE(devices_.FindDevice(provider.GetMacAddress()).has_value()); + fast_pair_seeker_.reset(); +} + TEST_F(FastPairSeekerImplTest, RetroactivePairingWithUserConsent) { NEARBY_LOG_SET_SEVERITY(VERBOSE); FakeProvider provider; diff --git a/fastpair/repository/BUILD b/fastpair/repository/BUILD index c6b3dff2..747a6462 100644 --- a/fastpair/repository/BUILD +++ b/fastpair/repository/BUILD @@ -81,6 +81,7 @@ cc_library( ":repository", "//fastpair/common", "//fastpair/proto:fastpair_cc_proto", + "//internal/base", "//internal/platform:types", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/status", @@ -115,6 +116,7 @@ cc_test( "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/strings:string_view", "@com_google_googletest//:gtest_main", ], ) diff --git a/fastpair/repository/fake_fast_pair_repository.cc b/fastpair/repository/fake_fast_pair_repository.cc index 4c0491d7..713a615d 100644 --- a/fastpair/repository/fake_fast_pair_repository.cc +++ b/fastpair/repository/fake_fast_pair_repository.cc @@ -18,16 +18,31 @@ #include #include #include +#include +#include "absl/status/status.h" #include "absl/strings/escaping.h" #include "absl/strings/string_view.h" #include "fastpair/common/account_key.h" +#include "fastpair/common/account_key_filter.h" #include "fastpair/common/constant.h" +#include "fastpair/common/device_metadata.h" #include "fastpair/common/fast_pair_device.h" +#include "fastpair/proto/data.proto.h" +#include "fastpair/proto/enum.proto.h" #include "fastpair/proto/fastpair_rpcs.proto.h" +#include "fastpair/repository/fast_pair_repository.h" namespace nearby { namespace fastpair { +void FakeFastPairRepository::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void FakeFastPairRepository::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + void FakeFastPairRepository::SetFakeMetadata(absl::string_view hex_model_id, proto::Device metadata) { proto::GetObservedDeviceResponse response; @@ -73,6 +88,14 @@ void FakeFastPairRepository::GetDeviceMetadata( }); } +void FakeFastPairRepository::GetUserSavedDevices() { + proto::OptInStatus opt_in_status = proto::OptInStatus::OPT_IN_STATUS_UNKNOWN; + std::vector saved_devices; + for (auto& observer : observers_.GetObservers()) { + observer->OnGetUserSavedDevices(opt_in_status, saved_devices); + } +} + void FakeFastPairRepository::WriteAccountAssociationToFootprints( FastPairDevice& device, OperationCallback callback) { executor_.Execute([callback = std::move(callback), this]() mutable { diff --git a/fastpair/repository/fake_fast_pair_repository.h b/fastpair/repository/fake_fast_pair_repository.h index 5afb3d24..ef1a7619 100644 --- a/fastpair/repository/fake_fast_pair_repository.h +++ b/fastpair/repository/fake_fast_pair_repository.h @@ -25,6 +25,7 @@ #include "fastpair/common/account_key.h" #include "fastpair/common/device_metadata.h" #include "fastpair/repository/fast_pair_repository.h" +#include "internal/base/observer_list.h" #include "internal/platform/single_thread_executor.h" namespace nearby { @@ -49,13 +50,13 @@ class FakeFastPairRepository : public FastPairRepository { void SetResultOfIsDeviceSavedToAccount(absl::Status status); // FastPairRepository:: - void AddObserver(Observer* observer) override{}; - void RemoveObserver(Observer* observer) override{}; + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; void GetDeviceMetadata(absl::string_view hex_model_id, DeviceMetadataCallback callback) override; - void GetUserSavedDevices() override{}; + void GetUserSavedDevices() override; void WriteAccountAssociationToFootprints(FastPairDevice& device, OperationCallback callback) override; @@ -82,6 +83,7 @@ class FakeFastPairRepository : public FastPairRepository { absl::Status deleted_associated_device_; // Results of IsDeviceSavedToAccount absl::Status is_device_saved_to_account_; + ObserverList observers_; SingleThreadExecutor executor_; }; } // namespace fastpair diff --git a/fastpair/repository/fast_pair_device_repository.cc b/fastpair/repository/fast_pair_device_repository.cc index d210d94d..e219a484 100644 --- a/fastpair/repository/fast_pair_device_repository.cc +++ b/fastpair/repository/fast_pair_device_repository.cc @@ -19,6 +19,8 @@ #include #include +#include "fastpair/common/account_key.h" +#include "fastpair/common/fast_pair_device.h" #include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" @@ -51,7 +53,7 @@ void FastPairDeviceRepository::RemoveDevice(const FastPairDevice* device) { for (auto* callback : observers_.GetObservers()) { (*callback)(*fast_pair_device); } - NEARBY_LOGS(VERBOSE) << "Destroyed FP device: " << fast_pair_device; + NEARBY_LOGS(VERBOSE) << "Destroyed FP device: " << *fast_pair_device; }); } @@ -70,6 +72,20 @@ std::optional FastPairDeviceRepository::FindDevice( } } +std::optional FastPairDeviceRepository::FindDevice( + const AccountKey& account_key) { + MutexLock lock(&mutex_); + auto it = std::find_if(devices_.begin(), devices_.end(), + [&](const std::unique_ptr& device) { + return device->GetAccountKey() == account_key; + }); + if (it != devices_.end()) { + return it->get(); + } else { + return std::nullopt; + } +} + std::unique_ptr FastPairDeviceRepository::ExtractDevice( const FastPairDevice* device) { MutexLock lock(&mutex_); diff --git a/fastpair/repository/fast_pair_device_repository.h b/fastpair/repository/fast_pair_device_repository.h index ad70bb32..5240122f 100644 --- a/fastpair/repository/fast_pair_device_repository.h +++ b/fastpair/repository/fast_pair_device_repository.h @@ -54,6 +54,9 @@ class FastPairDeviceRepository { // or BLE. std::optional FindDevice(absl::string_view mac_address); + // Finds a device matching the account key. + std::optional FindDevice(const AccountKey& account_key); + void AddObserver(RemoveDeviceCallback* observer) { observers_.AddObserver(observer); } diff --git a/fastpair/repository/fast_pair_device_repository_test.cc b/fastpair/repository/fast_pair_device_repository_test.cc index 3418ae4c..e476e1d0 100644 --- a/fastpair/repository/fast_pair_device_repository_test.cc +++ b/fastpair/repository/fast_pair_device_repository_test.cc @@ -17,9 +17,9 @@ #include #include -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "fastpair/common/account_key.h" #include "fastpair/common/fast_pair_device.h" #include "fastpair/common/protocol.h" #include "internal/platform/single_thread_executor.h" @@ -31,6 +31,7 @@ namespace { constexpr absl::string_view kModelId = "123456"; constexpr absl::string_view kBleAddress = "AA:BB:CC:DD:EE:FF"; constexpr absl::string_view kBtAddress = "12:34:56:78:90:AB"; +constexpr absl::string_view kAccountKey = "04b85786180add47fb81a04a8ce6b0de"; TEST(FastPairDeviceRepositoryTest, AddDevice) { SingleThreadExecutor executor; @@ -76,6 +77,24 @@ TEST(FastPairDeviceRepositoryTest, FindDeviceByBtAddress) { executor.Shutdown(); } +TEST(FastPairDeviceRepositoryTest, FindDeviceByAccountKey) { + SingleThreadExecutor executor; + FastPairDeviceRepository repo(&executor); + auto fast_pair_device = + std::make_unique(Protocol::kFastPairInitialPairing); + fast_pair_device->SetPublicAddress(kBtAddress); + fast_pair_device->SetAccountKey(AccountKey(kAccountKey)); + repo.AddDevice(std::move(fast_pair_device)); + + auto opt_device = repo.FindDevice(AccountKey(kAccountKey)); + + ASSERT_TRUE(opt_device.has_value()); + FastPairDevice* device = opt_device.value(); + ASSERT_NE(device, nullptr); + EXPECT_EQ(device->GetAccountKey().GetAsBytes(), kAccountKey); + executor.Shutdown(); +} + TEST(FastPairDeviceRepositoryTest, RemoveDevice) { SingleThreadExecutor executor; FastPairDeviceRepository repo(&executor); From 9c6a11eff1067aa5c789f77ce1798cffa80333df Mon Sep 17 00:00:00 2001 From: hai007 Date: Wed, 2 Aug 2023 16:54:21 -0700 Subject: [PATCH 059/128] [NP] Remove metadata creation from FakePresenceClient To make the nearby_presence_unittests.cc more percise, the test should create the metadata and pass it to the fake client instead of the client creating it. This change also allows for the unittests to use RunLoop instead of RunUntilIdeal in multiple places. Bug: b/285015071 PiperOrigin-RevId: 553297388 --- presence/fake_presence_client.cc | 26 ++++++-------------------- presence/fake_presence_client.h | 11 +++-------- 2 files changed, 9 insertions(+), 28 deletions(-) diff --git a/presence/fake_presence_client.cc b/presence/fake_presence_client.cc index a6fbe881..44e73440 100644 --- a/presence/fake_presence_client.cc +++ b/presence/fake_presence_client.cc @@ -23,17 +23,6 @@ #include "presence/presence_device.h" #include "presence/scan_request.h" -namespace { - -::nearby::internal::Metadata BuildTestMetadata() { - ::nearby::internal::Metadata metadata; - metadata.set_bluetooth_mac_address("01234567"); - metadata.set_device_name("Pepper's device"); - return metadata; -} - -} // namespace - namespace nearby { namespace presence { @@ -62,19 +51,16 @@ void FakePresenceClient::CallStartScanCallback(absl::Status status) { callback_.start_scan_cb(status); } -void FakePresenceClient::CallOnDiscovered() { - PresenceDevice device{BuildTestMetadata()}; - callback_.on_discovered_cb(std::move(device)); +void FakePresenceClient::CallOnDiscovered(PresenceDevice device) { + callback_.on_discovered_cb(device); } -void FakePresenceClient::CallOnUpdated() { - PresenceDevice device{BuildTestMetadata()}; - callback_.on_updated_cb(std::move(device)); +void FakePresenceClient::CallOnUpdated(PresenceDevice device) { + callback_.on_updated_cb(device); } -void FakePresenceClient::CallOnLost() { - PresenceDevice device{BuildTestMetadata()}; - callback_.on_lost_cb(std::move(device)); +void FakePresenceClient::CallOnLost(PresenceDevice device) { + callback_.on_lost_cb(device); } } // namespace presence diff --git a/presence/fake_presence_client.h b/presence/fake_presence_client.h index 7bb8fffe..b2a6d816 100644 --- a/presence/fake_presence_client.h +++ b/presence/fake_presence_client.h @@ -15,7 +15,6 @@ #ifndef THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_CLIENT_H_ #define THIRD_PARTY_NEARBY_PRESENCE_FAKE_PRESENCE_CLIENT_H_ - #include #include @@ -55,21 +54,17 @@ class FakePresenceClient : public PresenceClient { return std::nullopt; } - void SetNextScanSessionSuccess(bool success) { - next_scan_should_succeed_ = success; - } std::vector GetActiveScanSessions(); void CallStartScanCallback(absl::Status status); - void CallOnDiscovered(); - void CallOnUpdated(); - void CallOnLost(); + 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_; - bool next_scan_should_succeed_ = true; }; } // namespace presence From 3a88f4e21792f152cf79b1b17302e3d134412136 Mon Sep 17 00:00:00 2001 From: hai007 Date: Wed, 2 Aug 2023 17:17:20 -0700 Subject: [PATCH 060/128] record connection layer status code PiperOrigin-RevId: 553302833 --- connections/status.cc | 6 ++ connections/status.h | 11 ++-- .../Sources/GNCCoreAdapter.mm | 6 ++ .../NearbyCoreAdapter/Sources/GNCError.mm | 6 ++ .../NearbyCoreAdapter/GNCConnectionDelegate.h | 2 + .../Public/NearbyCoreAdapter/GNCError.h | 2 + proto/sharing_enums.proto | 55 +++++++++++++++++++ 7 files changed, 84 insertions(+), 4 deletions(-) diff --git a/connections/status.cc b/connections/status.cc index 976f455e..46216ab3 100644 --- a/connections/status.cc +++ b/connections/status.cc @@ -53,6 +53,12 @@ std::string Status::ToString() const { return "kWifiLanError"; case Status::kPayloadUnknown: return "kPayloadUnknown"; + case Status::kReset: + return "kReset"; + case Status::kTimeout: + return "kTimeout"; + case Status::kUnknown: + // fall through default: return "Unknown"; } diff --git a/connections/status.h b/connections/status.h index cc40005c..0c2c1fa0 100644 --- a/connections/status.h +++ b/connections/status.h @@ -22,7 +22,7 @@ namespace connections { // Protocol operation result: kSuccess, if operation was successful; // descriptive error code otherwise. -// LINT.IfChange +// LINT.IfChange(status_enum) struct Status { // Status is a struct, so it is possible to pass some context about failure, // by adding extra fields to it when necessary, and not change any of the @@ -44,6 +44,9 @@ struct Status { kBleError, kWifiLanError, kPayloadUnknown, + kReset, + kTimeout, + kUnknown, kNextValue, }; Value value{kError}; @@ -53,9 +56,9 @@ struct Status { std::string ToString() const; }; // LINT.ThenChange( -// //depot/google3/location/nearby/cpp/sharing/implementation/nearby_connections_manager.cc:24 -// //depot/google3/location/nearby/cpp/sharing/implementation/nearby_connections_types.h:46 -// //depot/google3/location/nearby/cpp/sharing/implementation/nearby_connections_types_test.cc +// ../sharing/nearby_connections_manager.cc:status_enum, +// ../sharing/nearby_connections_types.h:status_enum, +// ../sharing/nearby_connections_types_test.cc:status_enum // ) inline bool operator==(const Status& a, const Status& b) { diff --git a/connections/swift/NearbyCoreAdapter/Sources/GNCCoreAdapter.mm b/connections/swift/NearbyCoreAdapter/Sources/GNCCoreAdapter.mm index e4395758..ef72eded 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/GNCCoreAdapter.mm +++ b/connections/swift/NearbyCoreAdapter/Sources/GNCCoreAdapter.mm @@ -87,6 +87,12 @@ GNCStatus GNCStatusFromCppStatus(Status status) { return GNCStatusWifiLanError; case Status::kPayloadUnknown: return GNCStatusPayloadUnknown; + case Status::kReset: + return GNCStatusReset; + case Status::kTimeout: + return GNCStatusTimeout; + case Status::kUnknown: + return GNCStatusUnknown; case Status::kNextValue: return GNCStatusUnknown; } diff --git a/connections/swift/NearbyCoreAdapter/Sources/GNCError.mm b/connections/swift/NearbyCoreAdapter/Sources/GNCError.mm index 36c6d43e..db10fee7 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/GNCError.mm +++ b/connections/swift/NearbyCoreAdapter/Sources/GNCError.mm @@ -65,6 +65,12 @@ NSError *NSErrorFromCppStatus(Status status) { return [NSError errorWithDomain:GNCErrorDomain code:GNCErrorWifiLanError userInfo:nil]; case Status::kPayloadUnknown: return [NSError errorWithDomain:GNCErrorDomain code:GNCErrorPayloadUnknown userInfo:nil]; + case Status::kReset: + return [NSError errorWithDomain:GNCErrorDomain code:GNCErrorReset userInfo:nil]; + case Status::kTimeout: + return [NSError errorWithDomain:GNCErrorDomain code:GNCErrorTimeout userInfo:nil]; + case Status::kUnknown: + return [NSError errorWithDomain:GNCErrorDomain code:GNCErrorUnknown userInfo:nil]; case Status::kNextValue: return [NSError errorWithDomain:GNCErrorDomain code:GNCErrorUnknown userInfo:nil]; } diff --git a/connections/swift/NearbyCoreAdapter/Sources/Public/NearbyCoreAdapter/GNCConnectionDelegate.h b/connections/swift/NearbyCoreAdapter/Sources/Public/NearbyCoreAdapter/GNCConnectionDelegate.h index 85c6aed6..84d22a27 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/Public/NearbyCoreAdapter/GNCConnectionDelegate.h +++ b/connections/swift/NearbyCoreAdapter/Sources/Public/NearbyCoreAdapter/GNCConnectionDelegate.h @@ -35,6 +35,8 @@ typedef NS_CLOSED_ENUM(NSInteger, GNCStatus) { GNCStatusWifiLanError, GNCStatusPayloadUnknown, GNCStatusUnknown, + GNCStatusReset, + GNCStatusTimeout, }; /** diff --git a/connections/swift/NearbyCoreAdapter/Sources/Public/NearbyCoreAdapter/GNCError.h b/connections/swift/NearbyCoreAdapter/Sources/Public/NearbyCoreAdapter/GNCError.h index 4242b676..76952324 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/Public/NearbyCoreAdapter/GNCError.h +++ b/connections/swift/NearbyCoreAdapter/Sources/Public/NearbyCoreAdapter/GNCError.h @@ -38,4 +38,6 @@ typedef NS_ERROR_ENUM(GNCErrorDomain, GNCErrorCode){ GNCErrorBleError, GNCErrorWifiLanError, GNCErrorPayloadUnknown, + GNCErrorReset, + GNCErrorTimeout, } NS_SWIFT_NAME(NearbyError); diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index abb3478d..b27fb956 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -312,6 +312,61 @@ enum AttachmentTransmissionStatus { FAILED_NULL_CONNECTION_DISCONNECTED = 19; } +// Generic result status of NearbyConnections API calls. +enum ConnectionLayerStatus { + // No status is available + CONNECTION_LAYER_STATUS_UNKNOWN = 0; + // The operation was successful. + CONNECTION_LAYER_STATUS_SUCCESS = 1; + // The operation failed, without any more information. + CONNECTION_LAYER_STATUS_ERROR = 2; + // The app called an API method out of order (i.e. another method is expected + // to be called first). + CONNECTION_LAYER_STATUS_OUT_OF_ORDER_API_CALL = 3; + // The app already has active operations (advertising, discovering, or + // connected to other devices) with another Strategy. Stop these operations on + // the current Strategy before trying to advertise or discover with a new + // Strategy. + CONNECTION_LAYER_STATUS_ALREADY_HAVE_ACTIVE_STRATEGY = 4; + // The app is already advertising; call StopAdvertising() before trying to + // advertise again. + CONNECTION_LAYER_STATUS_ALREADY_ADVERTISING = 5; + // The app is already discovering; call StopDiscovery() before trying to + // discover again. + CONNECTION_LAYER_STATUS_ALREADY_DISCOVERING = 6; + // NC is already listening for incoming connections from remote endpoints. + CONNECTION_LAYER_STATUS_ALREADY_LISTENING = 7; + // An attempt to read from/write to a connected remote endpoint failed. If + // this occurs repeatedly, consider invoking DisconnectFromEndpoint(). + CONNECTION_LAYER_STATUS_END_POINT_IO_ERROR = 8; + // An attempt to interact with a remote endpoint failed because it's unknown + // to us -- it's either an endpoint that was never discovered, or an endpoint + // that never connected to us (both of which are indicative of bad input from + // the client app). + CONNECTION_LAYER_STATUS_END_POINT_UNKNOWN = 9; + // The remote endpoint rejected the connection request. + CONNECTION_LAYER_STATUS_CONNECTION_REJECTED = 10; + // The app is already connected to the specified endpoint. Multiple + // connections to a remote endpoint cannot be maintained simultaneously. + CONNECTION_LAYER_STATUS_ALREADY_CONNECTED_TO_END_POINT = 11; + // The remote endpoint is not connected; messages cannot be sent to it. + CONNECTION_LAYER_STATUS_NOT_CONNECTED_TO_END_POINT = 12; + // There was an error trying to use the device's Bluetooth capabilities. + CONNECTION_LAYER_STATUS_BLUETOOTH_ERROR = 13; + // There was an error trying to use the device's Bluetooth Low Energy + // capabilities. + CONNECTION_LAYER_STATUS_BLE_ERROR = 14; + // There was an error trying to use the device's Wi-Fi capabilities. + CONNECTION_LAYER_STATUS_WIFI_LAN_ERROR = 15; + // An attempt to interact with an in-flight Payload failed because it's + // unknown to us. + CONNECTION_LAYER_STATUS_PAYLOAD_UNKNOWN = 16; + // The connection was reset + CONNECTION_LAYER_STATUS_RESET = 17; + // The connection timed out + CONNECTION_LAYER_STATUS_TIMEOUT = 18; +} + // The status of processing attachments after receiver received payloads // successfully. enum ProcessReceivedAttachmentsStatus { From 0282ba7700f6a84d1d2cd82034a6065c26b75d82 Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Wed, 2 Aug 2023 18:41:35 -0700 Subject: [PATCH 061/128] Changed the hotspot connection interval PiperOrigin-RevId: 553319367 --- internal/platform/flags/nearby_platform_feature_flags.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/platform/flags/nearby_platform_feature_flags.h b/internal/platform/flags/nearby_platform_feature_flags.h index 0283353b..934574e8 100644 --- a/internal/platform/flags/nearby_platform_feature_flags.h +++ b/internal/platform/flags/nearby_platform_feature_flags.h @@ -15,6 +15,8 @@ #ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_FLAGS_NEARBY_PLATFORM_FEATURE_FLAGS_H_ #define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_FLAGS_NEARBY_PLATFORM_FEATURE_FLAGS_H_ +#include + #include "absl/strings/string_view.h" #include "internal/flags/flag.h" @@ -57,7 +59,7 @@ constexpr auto kWifiHotspotConnectionMaxRetries = // The interval between 2 connectin attempts. constexpr auto kWifiHotspotConnectionIntervalMillis = - flags::Flag(kConfigPackage, "45415887", 500); + flags::Flag(kConfigPackage, "45415887", 2000); // The connection timeout to remote Wi-Fi hotspot. constexpr auto kWifiHotspotConnectionTimeoutMillis = From 859dd1d1bda099e94ddfd9242e6849e2102a06bd Mon Sep 17 00:00:00 2001 From: hai007 Date: Thu, 3 Aug 2023 00:17:23 -0700 Subject: [PATCH 062/128] Add definition to Parsing Failed Type PiperOrigin-RevId: 553382949 --- proto/sharing_enums.proto | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index b27fb956..6b07294e 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -501,7 +501,12 @@ enum ScanType { // The type of parsing endpoint id failed type. enum ParsingFailedType { FAILED_UNKNOWN_TYPE = 0; + // NULL advertisement is returned due to sender failing to parse advertisement + // from endpointInfo byte stream from receiver advertisement. FAILED_PARSE_ADVERTISEMENT = 1; + // NULL shareTarget is returned due to sender failing to create shareTarget + // from a valid parsed advertisement stemming from issues in certificates, QR + // code tokens or device names. FAILED_CONVERT_SHARE_TARGET = 2; } From 903c8e07beb132d2ad8d60836b91e8d58db06dc4 Mon Sep 17 00:00:00 2001 From: hai007 Date: Thu, 3 Aug 2023 01:20:36 -0700 Subject: [PATCH 063/128] [Sharing Analytics]Correct AttachmentTransmissionStatus from FAILED_NULL_CONNECTION to FAILED_NULL_CONNECTION_DISCONNECTED if the transfer failure is caused by Wifi/Bluetooth setting off. PiperOrigin-RevId: 553396704 --- proto/sharing_enums.proto | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index 6b07294e..7e720d19 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -310,6 +310,13 @@ enum AttachmentTransmissionStatus { // Breakdowns of FAILED_NULL_CONNECTION (Desktop side) FAILED_NULL_CONNECTION_INIT_OUTGOING = 18; FAILED_NULL_CONNECTION_DISCONNECTED = 19; + + // Breakdowns of FAILED_NULL_CONNECTION (android side) + // Connection failed due to Wifi is disconnected or Bluetooth setting is off + // or user turn on airplane mode. + FAILED_NULL_CONNECTION_LOST_CONNECTIVITY = 20; + // Unexpected connection failure. + FAILED_NULL_CONNECTION_FAILURE = 21; } // Generic result status of NearbyConnections API calls. From 005f734d536a7c3b55c16a69c25fa761fe569e22 Mon Sep 17 00:00:00 2001 From: hai007 Date: Thu, 3 Aug 2023 03:03:00 -0700 Subject: [PATCH 064/128] [Sharing] add metadata for FAST_SHARE_SERVER_RESPONSE event. PiperOrigin-RevId: 553419234 --- proto/sharing_enums.proto | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index 7e720d19..3d2e9cee 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -496,6 +496,49 @@ enum ServerResponseState { SERVER_RESPONSE_NOT_CONNECTED_TO_INTERNET = 10; } +// The purpose of requesting the server request. +enum SyncPurpose { + SYNC_PURPOSE_UNKNOWN = 0; + // When NearbySharingChimeraService#sync() is called. + SYNC_PURPOSE_ON_DEMAND_SYNC = 1; + // Requested by chime notification. + SYNC_PURPOSE_CHIME_NOTIFICATION = 2; + // For reqular daily sync. + SYNC_PURPOSE_DAILY_SYNC = 3; + // Wen a device opts into Nearby Share. + SYNC_PURPOSE_OPT_IN_FIRST_SYNC = 4; + // Requested when Nearby Share automatically enables a device that shares + // a single account that has already opted in on another device. + SYNC_PURPOSE_CHECK_DEFAULT_OPT_IN = 5; + // When a device enables Nearby Share. + SYNC_PURPOSE_NEARBY_SHARE_ENABLED = 6; + // When a device is in fast init advertising. + SYNC_PURPOSE_SYNC_AT_FAST_INIT = 7; + // When device start discovery. + SYNC_PURPOSE_SYNC_AT_DISCOVERY = 8; + // When device tries to load valid private certificate. + SYNC_PURPOSE_SYNC_AT_LOAD_PRIVATE_CERTIFICATE = 9; + // When device start advertiseement. + SYNC_PURPOSE_SYNC_AT_ADVERTISEMENT = 10; + // When device contacts list changes. + SYNC_PURPOSE_CONTACT_LIST_CHANGE = 11; + // When showing the C11 banner in Neary Share setting. + SYNC_PURPOSE_SHOW_C11N_VIEW = 12; + // For regular check contact reachability. + SYNC_PURPOSE_REGULAR_CHECK_CONTACT_REACHABILITY = 13; + // When selected contacts list changes in visibility setting. + SYNC_PURPOSE_VISIBILITY_SELECTED_CONTACT_CHANGE = 14; + // When switching account. + SYNC_PURPOSE_ACCOUNT_CHANGE = 15; +} + +// The device role to trigger the server request. +enum ClientRole { + CLIENT_ROLE_UNKNOWN = 0; + CLIENT_ROLE_SENDER = 1; + CLIENT_ROLE_RECEIVER = 2; +} + // The type of Nearby Sharing scanning. enum ScanType { UNKNOWN_SCAN_TYPE = 0; From b798798f5f484a6b8434ab53adf89e4df147a443 Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Thu, 3 Aug 2023 10:03:21 -0700 Subject: [PATCH 065/128] Log medium type for discovered endpoint PiperOrigin-RevId: 553510472 --- connections/implementation/client_proxy.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/connections/implementation/client_proxy.cc b/connections/implementation/client_proxy.cc index ee83d3ec..e275b7a4 100644 --- a/connections/implementation/client_proxy.cc +++ b/connections/implementation/client_proxy.cc @@ -318,8 +318,11 @@ void ClientProxy::OnEndpointFound( MutexLock lock(&mutex_); NEARBY_LOGS(INFO) << "ClientProxy [Endpoint Found]: [enter] id=" - << endpoint_id << "; service=" << service_id << "; info=" - << absl::BytesToHexString(endpoint_info.data()); + << endpoint_id << "; service=" << service_id + << "; info=" << absl::BytesToHexString(endpoint_info.data()) + << "; medium=" + << location::nearby::proto::connections::Medium_Name( + medium); if (!IsDiscoveringServiceId(service_id)) { NEARBY_LOGS(INFO) << "ClientProxy [Endpoint Found]: Ignoring event for id=" << endpoint_id From e8c0ce3d89d6d2c136d3942ed6efa687d937812b Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Thu, 3 Aug 2023 11:25:34 -0700 Subject: [PATCH 066/128] Deprecate Fast Pair Mediator PiperOrigin-RevId: 553538010 --- fastpair/keyed_service/BUILD | 79 ----- fastpair/keyed_service/fast_pair_mediator.cc | 242 --------------- fastpair/keyed_service/fast_pair_mediator.h | 129 -------- .../fast_pair_mediator_factory.cc | 47 --- .../fast_pair_mediator_factory.h | 39 --- .../fast_pair_mediator_factory_test.cc | 37 --- .../keyed_service/fast_pair_mediator_test.cc | 290 ------------------ 7 files changed, 863 deletions(-) delete mode 100644 fastpair/keyed_service/BUILD delete mode 100644 fastpair/keyed_service/fast_pair_mediator.cc delete mode 100644 fastpair/keyed_service/fast_pair_mediator.h delete mode 100644 fastpair/keyed_service/fast_pair_mediator_factory.cc delete mode 100644 fastpair/keyed_service/fast_pair_mediator_factory.h delete mode 100644 fastpair/keyed_service/fast_pair_mediator_factory_test.cc delete mode 100644 fastpair/keyed_service/fast_pair_mediator_test.cc diff --git a/fastpair/keyed_service/BUILD b/fastpair/keyed_service/BUILD deleted file mode 100644 index b5046e28..00000000 --- a/fastpair/keyed_service/BUILD +++ /dev/null @@ -1,79 +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. - -licenses(["notice"]) - -cc_library( - name = "keyed_service", - srcs = [ - "fast_pair_mediator.cc", - "fast_pair_mediator_factory.cc", - ], - hdrs = [ - "fast_pair_mediator.h", - "fast_pair_mediator_factory.h", - ], - visibility = [ - "//fastpair:__subpackages__", - ], - deps = [ - "//fastpair/common", - "//fastpair/internal/mediums", - "//fastpair/pairing", - "//fastpair/repository", - "//fastpair/repository:device_repository", - "//fastpair/repository:repository_impl", - "//fastpair/scanning:scanner", - "//fastpair/server_access", - "//fastpair/ui:fast_pair_ui", - "//internal/account", - "//internal/auth:oauth_lib", - "//internal/auth:types", - "//internal/flags:nearby_flags", - "//internal/network:nearby_http_client", - "//internal/network:types", - "//internal/platform:base", - "//internal/platform:logging", - "//internal/platform:types", - "//internal/platform/flags:platform_flags", - "//internal/preferences", - "@com_google_absl//absl/status", - ], -) - -cc_test( - name = "fast_pair_mediator_test", - size = "small", - srcs = [ - "fast_pair_mediator_factory_test.cc", - "fast_pair_mediator_test.cc", - ], - shard_count = 16, - deps = [ - ":keyed_service", - "//fastpair/testing", - "//fastpair/ui:fast_pair_ui", - "//fastpair/ui:mock_fast_pair_ui", - "//internal/account:test_support", - "//internal/network:nearby_http_client", - "//internal/platform:test_util", - "//internal/platform:types", - "//internal/platform/implementation/g3", # build_cleaner: keep - "//internal/test", - "//internal/test/google3_only:test", - "@com_github_protobuf_matchers//protobuf-matchers", - "@com_google_absl//absl/strings", - "@com_google_googletest//:gtest_main", - ], -) diff --git a/fastpair/keyed_service/fast_pair_mediator.cc b/fastpair/keyed_service/fast_pair_mediator.cc deleted file mode 100644 index 16850e2c..00000000 --- a/fastpair/keyed_service/fast_pair_mediator.cc +++ /dev/null @@ -1,242 +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 "fastpair/keyed_service/fast_pair_mediator.h" - -#include -#include -#include -#include -#include - -#include "absl/status/status.h" -#include "fastpair/common/fast_pair_device.h" -#include "fastpair/common/fast_pair_prefs.h" -#include "fastpair/common/protocol.h" -#include "fastpair/internal/mediums/mediums.h" -#include "fastpair/pairing/pairer_broker_impl.h" -#include "fastpair/repository/fast_pair_device_repository.h" -#include "fastpair/repository/fast_pair_repository_impl.h" -#include "fastpair/scanning/scanner_broker_impl.h" -#include "fastpair/server_access/fast_pair_client_impl.h" -#include "fastpair/ui/actions.h" -#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" -#include "fastpair/ui/ui_broker_impl.h" -#include "internal/account/account_manager_impl.h" -#include "internal/flags/nearby_flags.h" -#include "internal/platform/feature_flags.h" -#include "internal/platform/flags/nearby_platform_feature_flags.h" -#include "internal/platform/logging.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/platform/task_runner_impl.h" -#include "internal/preferences/preferences_manager.h" - -namespace nearby { -namespace fastpair { -namespace { -constexpr char kFastPairPreferencesFilePath[] = "Google/Nearby/FastPair"; -constexpr FeatureFlags::Flags fast_pair_feature_flags = FeatureFlags::Flags{ - .enable_scan_for_fast_pair_advertisement = true, -}; -} - -Mediator::Mediator( - std::unique_ptr executor, - std::unique_ptr mediums, std::unique_ptr ui_broker, - std::unique_ptr notification_controller, - std::unique_ptr authentication_manager, - std::unique_ptr http_client, - std::unique_ptr device_info) - : executor_(std::move(executor)), - mediums_(std::move(mediums)), - ui_broker_(std::move(ui_broker)), - notification_controller_(std::move(notification_controller)), - authentication_manager_(std::move(authentication_manager)), - http_client_(std::move(http_client)), - device_info_(std::move(device_info)) { - NearbyFlags::GetInstance().OverrideBoolFlagValue( - platform::config_package_nearby::nearby_platform_feature:: - kEnableBleV2Gatt, - true); - const_cast(FeatureFlags::GetInstance()) - .SetFlags(fast_pair_feature_flags); - - devices_ = std::make_unique(executor_.get()); - scanner_broker_ = std::make_unique( - *mediums_, executor_.get(), devices_.get()); - task_runner_ = std::make_unique(1); - preferences_manager_ = std::make_unique( - kFastPairPreferencesFilePath); - account_manager_ = AccountManagerImpl::Factory::Create( - preferences_manager_.get(), prefs::kNearbyFastPairUsersName, - authentication_manager_.get(), task_runner_.get()); - fast_pair_client_ = std::make_unique( - authentication_manager_.get(), account_manager_.get(), http_client_.get(), - &fast_pair_http_notifier_, device_info_.get()); - fast_pair_repository_ = - std::make_unique(fast_pair_client_.get()); - pairer_broker_ = std::make_unique( - *mediums_, executor_.get(), account_manager_.get()); - scanner_broker_->AddObserver(this); - ui_broker_->AddObserver(this); - pairer_broker_->AddObserver(this); -} - -void Mediator::OnDeviceFound(FastPairDevice& device) { - NEARBY_LOGS(INFO) << __func__ << ": " << device; - if (device.ShouldShowUiNotification().value_or(false)) { - NEARBY_LOGS(INFO) << __func__ << ": Ignoring because show UI flag is false"; - return; - } - if (foreground_currently_showing_notification_) { - NEARBY_LOGS(VERBOSE) - << __func__ - << ": Already showing a notification for a different device= "; - return; - } - // Show discovery notification - foreground_currently_showing_notification_ = true; - ui_broker_->ShowDiscovery(device, *notification_controller_); -} - -void Mediator::OnDeviceLost(FastPairDevice& device) { - NEARBY_LOGS(INFO) << __func__ << ": " << device; -} - -void Mediator::OnDiscoveryAction(FastPairDevice& device, - DiscoveryAction action) { - switch (action) { - case DiscoveryAction::kPairToDevice: - NEARBY_LOGS(INFO) << __func__ << ": Action = kPairToDevice"; - pairer_broker_->PairDevice(device); - break; - case DiscoveryAction::kDismissedByOs: - NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByOs"; - break; - case DiscoveryAction::kDismissedByUser: - // When the user explicitly dismisses the discovery notification, update - // the device's block-list value accordingly. - NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByUser"; - foreground_currently_showing_notification_ = false; - // TODO(b/285453663): update discovery block list - break; - case DiscoveryAction::kDismissedByTimeout: - NEARBY_LOGS(INFO) << __func__ << ": Action = kDismissedByTimeout"; - foreground_currently_showing_notification_ = false; - break; - case DiscoveryAction::kLearnMore: - NEARBY_LOGS(INFO) << __func__ << ": Action = kLearnMore"; - break; - case DiscoveryAction::kDone: - NEARBY_LOGS(INFO) << __func__ << ": Action = kDone"; - foreground_currently_showing_notification_ = false; - break; - default: - NEARBY_LOGS(INFO) << __func__ << ": Action = Unknown"; - break; - } -} - -void Mediator::OnDevicePaired(FastPairDevice& device) { - NEARBY_LOGS(INFO) << __func__ << ": " << device; - ui_broker_->ShowPairingResult(device, *notification_controller_, true); -} - -void Mediator::OnAccountKeyWrite(FastPairDevice& device, - std::optional error) { - if (error.has_value()) { - NEARBY_LOGS(INFO) << __func__ << ": Device=" << device - << ",Error=" << error.value(); - return; - } - - NEARBY_LOGS(INFO) << __func__ << ": Device=" << device; - if (device.GetProtocol() == Protocol::kFastPairRetroactivePairing) { - // TODO: UI ShowAssociateAccount - } -} - -void Mediator::OnPairingComplete(FastPairDevice& device) { - NEARBY_LOGS(INFO) << __func__ << ": " << device; -} - -void Mediator::OnPairFailure(FastPairDevice& device, PairFailure failure) { - NEARBY_LOGS(INFO) << __func__ << ": " << device - << " with PairFailure: " << failure; - ui_broker_->ShowPairingResult(device, *notification_controller_, false); -} - -void Mediator::StartScanning() { - NEARBY_LOGS(VERBOSE) << __func__; - if (IsFastPairEnabled()) { - if (scanning_session_ != nullptr) { - return; - } - scanner_broker_ = std::make_unique( - *mediums_, executor_.get(), devices_.get()); - scanner_broker_->AddObserver(this); - scanning_session_ = - scanner_broker_->StartScanning(Protocol::kFastPairInitialPairing); - return; - } - scanning_session_.reset(); -} - -void Mediator::StopScanning() { - NEARBY_LOGS(VERBOSE) << __func__; - if (scanning_session_ == nullptr) { - return; - } - scanning_session_.reset(); - scanner_broker_->RemoveObserver(this); - DestroyOnExecutor(std::move(scanner_broker_), executor_.get()); -} - -bool Mediator::IsFastPairEnabled() { - // TODO(b/275452353): Add feature_status_tracker IsFastPairEnabled() - // Currently default to true. - NEARBY_LOGS(VERBOSE) << __func__ << ": " << true; - return true; -} - -void Mediator::SetIsScreenLocked(bool locked) { - executor_->Execute( - "on_lock_state_changed", - [this, locked]() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_) { - NEARBY_LOGS(INFO) << __func__ << ": Screen lock state changed. (" - << std::boolalpha << locked << ")"; - is_screen_locked_ = locked; - InvalidateScanningState(); - }); -} - -void Mediator::InvalidateScanningState() { - NEARBY_LOGS(INFO) << __func__; - // Stop scanning when screen is off. - if (is_screen_locked_) { - StopScanning(); - NEARBY_LOGS(VERBOSE) << __func__ - << ": Stopping scanning because the screen is locked."; - return; - } - - // TODO(b/275452353): Check if bluetooth and fast pair is enabled - - // Screen is on, Bluetooth is enabled, and Fast Pair is enabled, start - // scanning. - StartScanning(); -} - -} // namespace fastpair -} // namespace nearby diff --git a/fastpair/keyed_service/fast_pair_mediator.h b/fastpair/keyed_service/fast_pair_mediator.h deleted file mode 100644 index 16c4f6a2..00000000 --- a/fastpair/keyed_service/fast_pair_mediator.h +++ /dev/null @@ -1,129 +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_FASTPAIR_KEYED_SERVICE_FAST_PAIR_MEDIATOR_H_ -#define THIRD_PARTY_NEARBY_FASTPAIR_KEYED_SERVICE_FAST_PAIR_MEDIATOR_H_ - -#include -#include -#include - -#include "fastpair/common/fast_pair_device.h" -#include "fastpair/internal/mediums/mediums.h" -#include "fastpair/pairing/pairer_broker.h" -#include "fastpair/repository/fast_pair_device_repository.h" -#include "fastpair/scanning/scanner_broker.h" -#include "fastpair/server_access/fast_pair_client.h" -#include "fastpair/server_access/fast_pair_http_notifier.h" -#include "fastpair/repository/fast_pair_repository.h" -#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" -#include "fastpair/ui/ui_broker.h" -#include "internal/account/account_manager.h" -#include "internal/auth/authentication_manager.h" -#include "internal/network/http_client.h" -#include "internal/platform/device_info.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/platform/task_runner.h" -#include "internal/preferences/preferences_manager.h" - -namespace nearby { -namespace fastpair { - -// Implements the Mediator design pattern for the components in the Fast Pair -class Mediator final : public ScannerBroker::Observer, - public UIBroker::Observer, - public PairerBroker::Observer { - public: - Mediator( - std::unique_ptr executor, - std::unique_ptr mediums, std::unique_ptr ui_broker, - std::unique_ptr notification_controller, - std::unique_ptr authentication_manager, - std::unique_ptr http_client, - std::unique_ptr device_info); - Mediator(const Mediator&) = delete; - Mediator& operator=(const Mediator&) = delete; - ~Mediator() override { - if (scanning_session_ == nullptr) { - NEARBY_LOGS(ERROR) << __func__ << "scanner is not running"; - } - scanning_session_.reset(); - scanner_broker_->RemoveObserver(this); - DestroyOnExecutor(std::move(scanner_broker_), executor_.get()); - scanner_broker_.reset(); - ui_broker_->RemoveObserver(this); - ui_broker_.reset(); - }; - - AccountManager* GetAccountManager() { return account_manager_.get(); } - - FastPairNotificationController* GetNotificationController() { - return notification_controller_.get(); - } - - FastPairRepository* GetFastPairRepository() { - return fast_pair_repository_.get(); - } - - void StartScanning(); - void StopScanning(); - - // ScannerBroker::Observer - void OnDeviceFound(FastPairDevice& device) override; - void OnDeviceLost(FastPairDevice& device) override; - - // UIBroker::Observer - void OnDiscoveryAction(FastPairDevice& device, - DiscoveryAction action) override; - - // PairBroker:Observer - void OnDevicePaired(FastPairDevice& device) override; - void OnAccountKeyWrite(FastPairDevice& device, - std::optional error) override; - void OnPairingComplete(FastPairDevice& device) override; - void OnPairFailure(FastPairDevice& device, PairFailure failure) override; - - // Handle the state changes of screen lock. - void SetIsScreenLocked(bool is_locked); - - private: - bool IsFastPairEnabled(); - void InvalidateScanningState() ABSL_EXCLUSIVE_LOCKS_REQUIRED(*executor_); - bool IsDeviceCurrentlyShowingNotification(const FastPairDevice& device); - - bool foreground_currently_showing_notification_ = false; - FastPairHttpNotifier fast_pair_http_notifier_; - std::unique_ptr executor_; - std::unique_ptr mediums_; - std::unique_ptr scanner_broker_; - std::unique_ptr scanning_session_; - std::unique_ptr ui_broker_; - std::unique_ptr pairer_broker_; - std::unique_ptr notification_controller_; - std::unique_ptr fast_pair_repository_; - std::unique_ptr task_runner_; - std::unique_ptr devices_; - std::unique_ptr account_manager_; - std::unique_ptr preferences_manager_; - std::unique_ptr authentication_manager_; - std::unique_ptr fast_pair_client_; - std::unique_ptr http_client_; - std::unique_ptr device_info_; - bool is_screen_locked_ = false; -}; - -} // namespace fastpair -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_FASTPAIR_KEYED_SERVICE_FAST_PAIR_MEDIATOR_H_ diff --git a/fastpair/keyed_service/fast_pair_mediator_factory.cc b/fastpair/keyed_service/fast_pair_mediator_factory.cc deleted file mode 100644 index 5e87f8f1..00000000 --- a/fastpair/keyed_service/fast_pair_mediator_factory.cc +++ /dev/null @@ -1,47 +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 "fastpair/keyed_service/fast_pair_mediator_factory.h" - -#include - -#include "fastpair/internal/mediums/mediums.h" -#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" -#include "fastpair/ui/ui_broker_impl.h" -#include "internal/auth/authentication_manager_impl.h" -#include "internal/network/http_client_impl.h" -#include "internal/platform/device_info_impl.h" -#include "internal/platform/single_thread_executor.h" - -namespace nearby { -namespace fastpair { - -MediatorFactory* MediatorFactory::GetInstance() { - static MediatorFactory* instance = new MediatorFactory(); - return instance; -} - -Mediator* MediatorFactory::CreateMediator() { - mediator_ = std::make_unique( - std::make_unique(), std::make_unique(), - std::make_unique(), - std::make_unique(), - std::make_unique(), - std::make_unique(), - std::make_unique()); - return mediator_.get(); -} - -} // namespace fastpair -} // namespace nearby diff --git a/fastpair/keyed_service/fast_pair_mediator_factory.h b/fastpair/keyed_service/fast_pair_mediator_factory.h deleted file mode 100644 index f1117aed..00000000 --- a/fastpair/keyed_service/fast_pair_mediator_factory.h +++ /dev/null @@ -1,39 +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_FASTPAIR_KEYED_SERVICE_FAST_PAIR_MEDIATOR_FACTORY_H_ -#define THIRD_PARTY_NEARBY_FASTPAIR_KEYED_SERVICE_FAST_PAIR_MEDIATOR_FACTORY_H_ - -#include - -#include "fastpair/keyed_service/fast_pair_mediator.h" - -namespace nearby { -namespace fastpair { - -class MediatorFactory { - public: - // Return a singleton instance of Mediator Factory - static MediatorFactory* GetInstance(); - - Mediator* CreateMediator(); - - private: - std::unique_ptr mediator_; -}; - -} // namespace fastpair -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_FASTPAIR_KEYED_SERVICE_FAST_PAIR_MEDIATOR_FACTORY_H_ diff --git a/fastpair/keyed_service/fast_pair_mediator_factory_test.cc b/fastpair/keyed_service/fast_pair_mediator_factory_test.cc deleted file mode 100644 index 234112a2..00000000 --- a/fastpair/keyed_service/fast_pair_mediator_factory_test.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 "fastpair/keyed_service/fast_pair_mediator_factory.h" - -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" -#include "gtest/gtest.h" -#include "fastpair/keyed_service/fast_pair_mediator.h" - -namespace nearby { -namespace fastpair { -namespace { - -TEST(MediatorFactoryTest, checkMediatorCreateWithFactory) { - MediatorFactory *factory_ = nullptr; - Mediator *mediator_ = nullptr; - factory_ = MediatorFactory::GetInstance(); - EXPECT_THAT(factory_, testing::NotNull()); - mediator_ = factory_->CreateMediator(); - EXPECT_THAT(mediator_, testing::NotNull()); -} - -} // namespace -} // namespace fastpair -} // namespace nearby diff --git a/fastpair/keyed_service/fast_pair_mediator_test.cc b/fastpair/keyed_service/fast_pair_mediator_test.cc deleted file mode 100644 index bd832bb7..00000000 --- a/fastpair/keyed_service/fast_pair_mediator_test.cc +++ /dev/null @@ -1,290 +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 "fastpair/keyed_service/fast_pair_mediator.h" - -#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/string_view.h" -#include "fastpair/testing/fast_pair_service_data_creator.h" -#include "fastpair/ui/actions.h" -#include "fastpair/ui/fast_pair/fast_pair_notification_controller.h" -#include "fastpair/ui/mock_ui_broker.h" -#include "fastpair/ui/ui_broker.h" -#include "internal/account/fake_account_manager.h" -#include "internal/network/http_client_impl.h" -#include "internal/platform/device_info_impl.h" -#include "internal/platform/medium_environment.h" -#include "internal/platform/single_thread_executor.h" -#include "internal/test/fake_device_info.h" -#include "internal/test/fake_http_client.h" -#include "internal/test/google3_only/fake_authentication_manager.h" - -namespace nearby { -namespace fastpair { -namespace { - -constexpr absl::string_view kModelId = "718c17"; -constexpr absl::string_view kServiceID = "Fast Pair"; -constexpr absl::string_view kFastPairServiceUuid = - "0000FE2C-0000-1000-8000-00805F9B34FB"; -constexpr int kNotDiscoverableAdvHeader = 0b00000110; -constexpr int kAccountKeyFilterHeader = 0b01100000; -constexpr int kSaltHeader = 0b00010001; -constexpr absl::string_view kAccountKeyFilter("112233445566"); -constexpr absl::string_view kSalt("01"); - -constexpr absl::string_view kModelId2 = "9adb11"; -constexpr absl::string_view kAddress = "74:74:46:01:6C:21"; - -class MediatorTest : public testing::Test { - public: - MediatorTest() { - AccountManagerImpl::Factory::SetFactoryForTesting( - &account_manager_factory_); - http_client_ = std::make_unique(); - device_info_ = std::make_unique(); - authentication_manager_ = - std::make_unique(); - } - void SetUp() override { - env_.Start(); - GetAuthManager()->EnableSyncMode(); - mediums_ = std::make_unique(); - ui_broker_ = std::make_unique(); - mock_ui_broker_ = static_cast(ui_broker_.get()); - - notification_controller_ = - std::make_unique(); - - executor_ = std::make_unique(); - } - - void TearDown() override { - executor_.reset(); - mediums_.reset(); - ui_broker_.reset(); - mock_ui_broker_ = nullptr; - notification_controller_.reset(); - mediator_.reset(); - env_.Stop(); - } - - nearby::FakeAuthenticationManager* GetAuthManager() { - return reinterpret_cast( - authentication_manager_.get()); - } - - network::FakeHttpClient* GetHttpClient() { - return reinterpret_cast(http_client_.get()); - } - - void SetUpDeviceMetadata() { - proto::GetObservedDeviceResponse response_proto; - auto* device = response_proto.mutable_device(); - int64_t device_id; - CHECK(absl::SimpleHexAtoi(kModelId, &device_id)); - device->set_id(device_id); - network::HttpResponse response; - response.SetStatusCode(network::HttpStatusCode::kHttpOk); - response.SetBody(response_proto.SerializeAsString()); - GetHttpClient()->SetResponseForSyncRequest(response); - } - - protected: - MediumEnvironment& env_{MediumEnvironment::Instance()}; - std::unique_ptr mediums_; - std::unique_ptr ui_broker_; - std::unique_ptr notification_controller_; - std::unique_ptr executor_; - MockUIBroker* mock_ui_broker_; - std::unique_ptr mediator_; - FakeAccountManager::Factory account_manager_factory_; - std::unique_ptr authentication_manager_; - std::unique_ptr http_client_; - std::unique_ptr device_info_; -}; - -TEST_F(MediatorTest, StartScanningFoundDevice) { - SetUpDeviceMetadata(); - // Create Fast Pair Mediator - mediator_ = std::make_unique( - std::move(executor_), std::move(mediums_), std::move(ui_broker_), - std::move(notification_controller_), std::move(authentication_manager_), - std::move(http_client_), std::move(device_info_)); - absl::Notification done; - EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).WillOnce([&done] { - done.Notify(); - }); - - // Create Advertiser and startAdvertising - Mediums mediums_advertiser; - std::string service_id(kServiceID); - ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; - std::string fast_pair_service_uuid(kFastPairServiceUuid); - mediums_advertiser.GetBle().GetMedium().StartAdvertising( - service_id, advertisement_bytes, fast_pair_service_uuid); - - mediator_->StartScanning(); - done.WaitForNotification(); -} - -TEST_F(MediatorTest, StartScanningFoundDifferentDeviceWhenDisplaying) { - SetUpDeviceMetadata(); - // Create Fast Pair Mediator - mediator_ = std::make_unique( - std::move(executor_), std::move(mediums_), std::move(ui_broker_), - std::move(notification_controller_), std::move(authentication_manager_), - std::move(http_client_), std::move(device_info_)); - absl::Notification done; - EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1).WillOnce([&done] { - done.Notify(); - }); - - // Create Advertiser and startAdvertising - Mediums mediums_advertiser; - std::string service_id(kServiceID); - ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; - std::string fast_pair_service_uuid(kFastPairServiceUuid); - mediums_advertiser.GetBle().GetMedium().StartAdvertising( - service_id, advertisement_bytes, fast_pair_service_uuid); - - // Create a different Advertiser and startAdvertising to cause confliction - Mediums mediums_advertiser2; - ByteArray advertisement_bytes2{absl::HexStringToBytes(kModelId2)}; - mediums_advertiser2.GetBle().GetMedium().StartAdvertising( - service_id, advertisement_bytes2, fast_pair_service_uuid); - - mediator_->StartScanning(); - done.WaitForNotification(); -} - -TEST_F(MediatorTest, StartScanningFoundSameDeviceWhenDisplaying) { - SetUpDeviceMetadata(); - // Create Fast Pair Mediator - mediator_ = std::make_unique( - std::move(executor_), std::move(mediums_), std::move(ui_broker_), - std::move(notification_controller_), std::move(authentication_manager_), - std::move(http_client_), std::move(device_info_)); - absl::Notification done; - EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1).WillOnce([&done] { - done.Notify(); - }); - - // Create Advertiser and startAdvertising - Mediums mediums_advertiser; - std::string service_id(kServiceID); - ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; - std::string fast_pair_service_uuid(kFastPairServiceUuid); - mediums_advertiser.GetBle().GetMedium().StartAdvertising( - service_id, advertisement_bytes, fast_pair_service_uuid); - - // Create another same Advertiser and startAdvertising to cause confliction - Mediums mediums_advertiser2; - ByteArray advertisement_bytes2{absl::HexStringToBytes(kModelId)}; - mediums_advertiser2.GetBle().GetMedium().StartAdvertising( - service_id, advertisement_bytes2, fast_pair_service_uuid); - - mediator_->StartScanning(); - done.WaitForNotification(); -} - -TEST_F(MediatorTest, - StartScanningForSubsequentPairingFoundSameDeviceWhenDisplaying) { - SetUpDeviceMetadata(); - // Create Fast Pair Mediator - mediator_ = std::make_unique( - std::move(executor_), std::move(mediums_), std::move(ui_broker_), - std::move(notification_controller_), std::move(authentication_manager_), - std::move(http_client_), std::move(device_info_)); - absl::Notification done; - EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(1).WillOnce([&done] { - done.Notify(); - }); - - // Create Advertiser and advertising discoverable advertisement - Mediums mediums_advertiser; - std::string service_id(kServiceID); - ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; - std::string fast_pair_service_uuid(kFastPairServiceUuid); - mediums_advertiser.GetBle().GetMedium().StartAdvertising( - service_id, advertisement_bytes, fast_pair_service_uuid); - - // Create another same Advertiser and advertising non-discoverable - // advertisement to cause confliction - Mediums mediums_advertiser2; - std::vector bytes = FastPairServiceDataCreator::Builder() - .SetHeader(kNotDiscoverableAdvHeader) - .SetModelId(kModelId) - .AddExtraFieldHeader(kAccountKeyFilterHeader) - .AddExtraField(kAccountKeyFilter) - .AddExtraFieldHeader(kSaltHeader) - .AddExtraField(kSalt) - .Build() - ->CreateServiceData(); - ByteArray advertisement_bytes2{std::string(bytes.begin(), bytes.end())}; - mediums_advertiser2.GetBle().GetMedium().StartAdvertising( - service_id, advertisement_bytes2, fast_pair_service_uuid); - - mediator_->StartScanning(); - done.WaitForNotification(); -} - -TEST_F(MediatorTest, OnDiscoveryActionClicked) { - SetUpDeviceMetadata(); - SetUpDeviceMetadata(); - // Create Fast Pair Mediator - mediator_ = std::make_unique( - std::move(executor_), std::move(mediums_), std::move(ui_broker_), - std::move(notification_controller_), std::move(authentication_manager_), - std::move(http_client_), std::move(device_info_)); - - absl::Notification done; - EXPECT_CALL(*mock_ui_broker_, ShowDiscovery).Times(2).WillOnce([&done] { - done.Notify(); - }); - - // Create Advertiser and startAdvertising - Mediums mediums_advertiser; - std::string service_id(kServiceID); - ByteArray advertisement_bytes{absl::HexStringToBytes(kModelId)}; - std::string fast_pair_service_uuid(kFastPairServiceUuid); - mediums_advertiser.GetBle().GetMedium().StartAdvertising( - service_id, advertisement_bytes, fast_pair_service_uuid); - - mediator_->StartScanning(); - done.WaitForNotification(); - - FastPairDevice device(kModelId, kAddress, Protocol::kFastPairInitialPairing); - mock_ui_broker_->NotifyDiscoveryAction(device, - DiscoveryAction::kDismissedByTimeout); - // Create another same Advertiser and startAdvertising to cause confliction - Mediums mediums_advertiser2; - ByteArray advertisement_bytes2{absl::HexStringToBytes(kModelId)}; - mediums_advertiser2.GetBle().GetMedium().StartAdvertising( - service_id, advertisement_bytes2, fast_pair_service_uuid); - - done.WaitForNotification(); -} - -} // namespace -} // namespace fastpair -} // namespace nearby From f18ab078739ed8fb95daff032c6bf4275742d1a7 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Thu, 3 Aug 2023 16:21:09 -0700 Subject: [PATCH 067/128] Fix FastPairScannerImplTest The scanner must not be destroyed while there are still scanner's tasks running on the background executor. PiperOrigin-RevId: 553621813 --- .../fastpair/fast_pair_scanner_impl_test.cc | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/fastpair/scanning/fastpair/fast_pair_scanner_impl_test.cc b/fastpair/scanning/fastpair/fast_pair_scanner_impl_test.cc index 80c9c254..184bd3aa 100644 --- a/fastpair/scanning/fastpair/fast_pair_scanner_impl_test.cc +++ b/fastpair/scanning/fastpair/fast_pair_scanner_impl_test.cc @@ -16,6 +16,7 @@ #include #include +#include #include "gtest/gtest.h" #include "absl/strings/escaping.h" @@ -26,6 +27,7 @@ #include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" +#include "internal/platform/single_thread_executor.h" namespace nearby { namespace fastpair { @@ -61,18 +63,16 @@ class FastPairScannerObserver : public FastPairScanner::Observer { }; class FastPairScannerImplTest : public testing::Test { - protected: - MediumEnvironment& env_{MediumEnvironment::Instance()}; - - SingleThreadExecutor executor_; + public: + void SetUp() override { MediumEnvironment::Instance().Start(); } + void TearDown() override { MediumEnvironment::Instance().Stop(); } }; TEST_F(FastPairScannerImplTest, StartScanning) { - env_.Start(); - // Create Fast Pair Scanner and add its observer Mediums mediums_1; - auto scanner = std::make_unique(mediums_1, &executor_); + SingleThreadExecutor executor; + auto scanner = std::make_unique(mediums_1, &executor); CountDownLatch accept_latch(1); CountDownLatch lost_latch(1); FastPairScannerObserver observer(scanner.get(), &accept_latch, &lost_latch); @@ -95,14 +95,14 @@ TEST_F(FastPairScannerImplTest, StartScanning) { // Notify device lost EXPECT_TRUE(lost_latch.Await(kTaskWaitTimeout).result()); scan_session.reset(); - env_.Stop(); + DestroyOnExecutor(std::move(scanner), &executor); } TEST_F(FastPairScannerImplTest, StopScanning) { - env_.Start(); // Create Fast Pair Scanner and add its observer Mediums mediums_1; - auto scanner = std::make_unique(mediums_1, &executor_); + SingleThreadExecutor executor; + auto scanner = std::make_unique(mediums_1, &executor); CountDownLatch accept_latch(1); CountDownLatch lost_latch(1); FastPairScannerObserver observer(scanner.get(), &accept_latch, &lost_latch); @@ -120,7 +120,7 @@ TEST_F(FastPairScannerImplTest, StopScanning) { mediums_2.GetBle().GetMedium().StopAdvertising(service_id); // Device lost event should not be delivered when scan session has terminated. EXPECT_FALSE(lost_latch.Await(kShortTimeout).result()); - env_.Stop(); + DestroyOnExecutor(std::move(scanner), &executor); } } // namespace From 5b155f9a4af5fbeb2acbd63afcf1dfdcee1c5fd8 Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Thu, 3 Aug 2023 19:37:10 -0700 Subject: [PATCH 068/128] Added data section filter in BLE scanning PiperOrigin-RevId: 553660098 --- .../platform/implementation/windows/ble_v2.cc | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/internal/platform/implementation/windows/ble_v2.cc b/internal/platform/implementation/windows/ble_v2.cc index afe45ecc..eb607be1 100644 --- a/internal/platform/implementation/windows/ble_v2.cc +++ b/internal/platform/implementation/windows/ble_v2.cc @@ -47,6 +47,7 @@ #include "winrt/Windows.Devices.Bluetooth.Advertisement.h" #include "winrt/Windows.Devices.Bluetooth.h" #include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Storage.Streams.h" // NOLINT(misc-include-cleaner) namespace nearby { namespace windows { @@ -68,6 +69,8 @@ using ::winrt::Windows::Devices::Bluetooth::Advertisement:: BluetoothLEAdvertisementDataSection; using ::winrt::Windows::Devices::Bluetooth::Advertisement:: BluetoothLEAdvertisementDataTypes; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementFilter; // NOLINT(misc-include-cleaner) using ::winrt::Windows::Devices::Bluetooth::Advertisement:: BluetoothLEAdvertisementPublisher; using ::winrt::Windows::Devices::Bluetooth::Advertisement:: @@ -230,9 +233,19 @@ bool BleV2Medium::StartScanning(const Uuid& service_uuid, // Active mode indicates that scan request packets will be sent to query for // Scan Response watcher_.ScanningMode(BluetoothLEScanningMode::Active); - ::winrt::Windows::Devices::Bluetooth::BluetoothSignalStrengthFilter filter; - filter.SamplingInterval(TimeSpan(std::chrono::seconds(2))); - watcher_.SignalStrengthFilter(filter); + + BluetoothLEAdvertisementDataSection data_section; + data_section.DataType(0x16); + DataWriter data_writer; // NOLINT(misc-include-cleaner) + std::array service_id_data = service_uuid_.data(); + data_writer.WriteByte(service_id_data[3] & 0xff); + data_writer.WriteByte(service_id_data[2] & 0xff); + data_section.Data(data_writer.DetachBuffer()); + BluetoothLEAdvertisement advertisement; + advertisement.DataSections().Append(data_section); + BluetoothLEAdvertisementFilter advertisement_filter; + advertisement_filter.Advertisement(advertisement); + watcher_.AdvertisementFilter(advertisement_filter); watcher_.Start(); is_watcher_started_ = true; From d19f21ed71c82ef7f0df3ddae95b32dfbffbaa1c Mon Sep 17 00:00:00 2001 From: hai007 Date: Wed, 9 Aug 2023 09:29:19 -0700 Subject: [PATCH 069/128] [Sharing] Log Decrypt Certificate Failure Status. PiperOrigin-RevId: 555186404 --- proto/sharing_enums.proto | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index 3d2e9cee..13a0fd00 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -30,7 +30,7 @@ option objc_class_prefix = "GNSHP"; // in NearbyClearcutLogger (for android, or clearcut_event_logger as the // equivalence for Windows) for all events (may exclude settings), and // session_id for a pair of events (start and end of a session). -// Next id: 64 +// Next id: 65 enum EventType { UNKNOWN_EVENT_TYPE = 0; @@ -235,6 +235,9 @@ enum EventType { // User sets account preference SET_ACCOUNT = 63; + // Decrypt certificate failure + DECRYPT_CERTIFICATE_FAILURE = 64; + // LINT.ThenChange(//depot/google3/location/nearby/proto/nearby_event_codes.proto:SharingEventCode) } @@ -762,6 +765,16 @@ enum DesktopTransferEventType { DESKTOP_TRANSFER_EVENT_SEND_TYPE_SENT = 8; DESKTOP_TRANSFER_EVENT_SEND_TYPE_ERROR = 9; } + +enum DecryptCertificateFailureStatus { + DECRYPT_CERT_UNKNOWN_FAILURE = 0; + DECRYPT_CERT_NO_SUCH_ALGORITHM_FAILURE = 1; + DECRYPT_CERT_NO_SUCH_PADDING_FAILURE = 2; + DECRYPT_CERT_INVALID_KEY_FAILURE = 3; + DECRYPT_CERT_INVALID_ALGORITHM_PARAMETER_FAILURE = 4; + DECRYPT_CERT_ILLEGAL_BLOCK_SIZE_FAILURE = 5; + DECRYPT_CERT_BAD_PADDING_FAILURE = 6; +} // LINT.ThenChange( // //depot/google3/location/nearby/cpp/sharing/clients/dart/platform/lib/types/models.dart // ) From 5393daf9f174af4078c536e185103d0d7eb3e6cb Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Wed, 9 Aug 2023 13:43:12 -0700 Subject: [PATCH 070/128] Merge logging and types build targets Both "types" and "logging" build targets included "logging.h" Merging them together avoids circular dependencies. PiperOrigin-RevId: 555262816 --- connections/BUILD | 2 - connections/c/BUILD | 2 +- connections/implementation/BUILD | 2 - connections/implementation/analytics/BUILD | 2 - connections/implementation/mediums/BUILD | 1 - .../implementation/mediums/ble_v2/BUILD | 1 - .../implementation/mediums/webrtc/BUILD | 1 - fastpair/BUILD | 1 - fastpair/common/BUILD | 2 +- fastpair/crypto/BUILD | 2 +- fastpair/dataparser/BUILD | 2 +- fastpair/handshake/BUILD | 3 -- fastpair/internal/mediums/BUILD | 1 - fastpair/message_stream/BUILD | 4 -- fastpair/proto/BUILD | 4 +- fastpair/repository/BUILD | 2 - fastpair/retroactive/BUILD | 2 +- fastpair/scanning/BUILD | 1 - fastpair/scanning/fastpair/BUILD | 1 - fastpair/server_access/BUILD | 1 - fastpair/ui/BUILD | 2 +- internal/data/BUILD | 2 +- internal/network/BUILD | 1 - internal/platform/BUILD | 39 ++++--------------- internal/platform/implementation/g3/BUILD | 3 +- .../platform/implementation/windows/BUILD | 4 +- internal/proto/analytics/BUILD | 2 +- presence/BUILD | 4 +- presence/fpp/BUILD | 2 +- presence/implementation/BUILD | 1 - 30 files changed, 23 insertions(+), 74 deletions(-) diff --git a/connections/BUILD b/connections/BUILD index 04cc0ff9..a8f9d09d 100644 --- a/connections/BUILD +++ b/connections/BUILD @@ -35,7 +35,6 @@ cc_library( "//internal/analytics:event_logger", "//internal/interop:device", "//internal/platform:base", - "//internal/platform:logging", "//internal/platform:types", "@com_google_absl//absl/strings", "@com_google_absl//absl/time", @@ -102,7 +101,6 @@ cc_test( "//connections/implementation:internal_test", "//connections/v3:v3_types", "//internal/platform:base", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", diff --git a/connections/c/BUILD b/connections/c/BUILD index c248c76e..17db7c4f 100644 --- a/connections/c/BUILD +++ b/connections/c/BUILD @@ -119,7 +119,7 @@ cc_test( ], deps = [ "//connections:core", - "//internal/platform:logging", + "//internal/platform:types", "//internal/platform/implementation/windows", "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_googletest//:gtest_main", diff --git a/connections/implementation/BUILD b/connections/implementation/BUILD index bb8eb3e5..2179906b 100644 --- a/connections/implementation/BUILD +++ b/connections/implementation/BUILD @@ -126,7 +126,6 @@ cc_library( "//internal/platform:comm", "//internal/platform:connection_info", "//internal/platform:error_code_recorder", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform:util", "//internal/platform/implementation:comm", @@ -236,7 +235,6 @@ cc_test( "//internal/interop:device", "//internal/platform:base", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep diff --git a/connections/implementation/analytics/BUILD b/connections/implementation/analytics/BUILD index 63989a25..022bb0cd 100644 --- a/connections/implementation/analytics/BUILD +++ b/connections/implementation/analytics/BUILD @@ -31,7 +31,6 @@ cc_library( "//connections:core_types", "//internal/analytics:event_logger", "//internal/platform:error_code_recorder", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform/implementation:types", "//internal/proto/analytics:connections_log_cc_proto", @@ -61,7 +60,6 @@ cc_test( "//internal/platform:base", "//internal/platform:comm", "//internal/platform:error_code_recorder", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "//internal/proto/analytics:connections_log_cc_proto", diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD index 61f968b4..2ef5be09 100644 --- a/connections/implementation/mediums/BUILD +++ b/connections/implementation/mediums/BUILD @@ -55,7 +55,6 @@ cc_library( "//internal/platform:base", "//internal/platform:cancellation_flag", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform:uuid", "//proto/mediums:web_rtc_signaling_frames_cc_proto", diff --git a/connections/implementation/mediums/ble_v2/BUILD b/connections/implementation/mediums/ble_v2/BUILD index 69d727eb..cd9321e8 100644 --- a/connections/implementation/mediums/ble_v2/BUILD +++ b/connections/implementation/mediums/ble_v2/BUILD @@ -45,7 +45,6 @@ cc_library( "//internal/flags:nearby_flags", "//internal/platform:base", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform:util", "//internal/platform:uuid", diff --git a/connections/implementation/mediums/webrtc/BUILD b/connections/implementation/mediums/webrtc/BUILD index 71e5abd2..2d15ee8d 100644 --- a/connections/implementation/mediums/webrtc/BUILD +++ b/connections/implementation/mediums/webrtc/BUILD @@ -39,7 +39,6 @@ cc_library( "//connections/implementation/mediums:utils", "//internal/platform:base", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:types", "//proto/mediums:web_rtc_signaling_frames_cc_proto", # TODO: Support WebRTC diff --git a/fastpair/BUILD b/fastpair/BUILD index ec101847..453b6db5 100644 --- a/fastpair/BUILD +++ b/fastpair/BUILD @@ -134,7 +134,6 @@ cc_test( "//fastpair/plugins:fake_fast_pair_plugin", "//internal/account:test_support", "//internal/network:types", - "//internal/platform:logging", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep diff --git a/fastpair/common/BUILD b/fastpair/common/BUILD index c657c1c4..c377b405 100644 --- a/fastpair/common/BUILD +++ b/fastpair/common/BUILD @@ -33,7 +33,7 @@ cc_library( deps = [ "//fastpair/proto:fastpair_cc_proto", "//internal/crypto_cros", - "//internal/platform:logging", + "//internal/platform:types", "//internal/preferences", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_map", diff --git a/fastpair/crypto/BUILD b/fastpair/crypto/BUILD index cac861f5..674826a6 100644 --- a/fastpair/crypto/BUILD +++ b/fastpair/crypto/BUILD @@ -21,7 +21,7 @@ cc_library( "//fastpair/common", "//internal/base:bluetooth_address", "//internal/platform:base", - "//internal/platform:logging", + "//internal/platform:types", "@boringssl//:crypto", "@com_google_absl//absl/log:check", ], diff --git a/fastpair/dataparser/BUILD b/fastpair/dataparser/BUILD index 58a5f9b6..76f520e6 100644 --- a/fastpair/dataparser/BUILD +++ b/fastpair/dataparser/BUILD @@ -17,7 +17,7 @@ cc_library( "//fastpair/crypto", "//internal/base:bluetooth_address", "//internal/platform:base", - "//internal/platform:logging", + "//internal/platform:types", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/strings", ], diff --git a/fastpair/handshake/BUILD b/fastpair/handshake/BUILD index 1e44021b..c845dac1 100644 --- a/fastpair/handshake/BUILD +++ b/fastpair/handshake/BUILD @@ -43,7 +43,6 @@ cc_library( "//fastpair/repository", "//internal/base:bluetooth_address", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform:uuid", "@boringssl//:crypto", @@ -89,7 +88,6 @@ cc_test( "//fastpair/internal/mediums", "//fastpair/repository:test_support", "//fastpair/testing", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", @@ -164,7 +162,6 @@ cc_test( ":handshake", "//fastpair/common", "//fastpair/internal/mediums", - "//internal/platform:logging", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep diff --git a/fastpair/internal/mediums/BUILD b/fastpair/internal/mediums/BUILD index 561b6f1e..589e6004 100644 --- a/fastpair/internal/mediums/BUILD +++ b/fastpair/internal/mediums/BUILD @@ -37,7 +37,6 @@ cc_library( deps = [ "//fastpair/common", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform/implementation:comm", "@com_google_absl//absl/base:core_headers", diff --git a/fastpair/message_stream/BUILD b/fastpair/message_stream/BUILD index 41969904..0ec782b2 100644 --- a/fastpair/message_stream/BUILD +++ b/fastpair/message_stream/BUILD @@ -60,7 +60,6 @@ cc_library( "//fastpair/common", "//internal/platform:base", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform:uuid", @@ -106,7 +105,6 @@ cc_library( ":message_stream", "//fastpair/common", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:test_util", "//internal/platform:types", "@com_google_absl//absl/status", @@ -128,7 +126,6 @@ cc_test( ":message_stream", "//fastpair/common", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep @@ -153,7 +150,6 @@ cc_test( ":message_stream", "//fastpair/common", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation:types", diff --git a/fastpair/proto/BUILD b/fastpair/proto/BUILD index ea2c5e6c..1ef59ebb 100644 --- a/fastpair/proto/BUILD +++ b/fastpair/proto/BUILD @@ -37,7 +37,7 @@ cc_library( deps = [ ":fastpair_cc_proto", "//fastpair/common", - "//internal/platform:logging", + "//internal/platform:types", "@com_google_absl//absl/strings", "@nlohmann_json//:json", ], @@ -57,7 +57,7 @@ cc_library( ":fastpair_cc_proto", "//fastpair/common", "//fastpair/repository", - "//internal/platform:logging", + "//internal/platform:types", "@com_google_absl//absl/time", ], ) diff --git a/fastpair/repository/BUILD b/fastpair/repository/BUILD index 747a6462..1f1afc60 100644 --- a/fastpair/repository/BUILD +++ b/fastpair/repository/BUILD @@ -42,7 +42,6 @@ cc_library( "//fastpair/proto:proto_builder", "//fastpair/server_access", "//internal/base", - "//internal/platform:logging", "//internal/platform:types", "@com_google_absl//absl/status", "@com_google_absl//absl/strings", @@ -62,7 +61,6 @@ cc_library( deps = [ "//fastpair/common", "//internal/base", - "//internal/platform:logging", "//internal/platform:types", "@com_google_absl//absl/functional:any_invocable", ], diff --git a/fastpair/retroactive/BUILD b/fastpair/retroactive/BUILD index 33884bf9..63315b12 100644 --- a/fastpair/retroactive/BUILD +++ b/fastpair/retroactive/BUILD @@ -65,8 +65,8 @@ cc_test( "//internal/base:bluetooth_address", "//internal/platform:base", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:test_util", + "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "@com_github_protobuf_matchers//protobuf-matchers", "@com_google_absl//absl/status", diff --git a/fastpair/scanning/BUILD b/fastpair/scanning/BUILD index 3478b93e..c4b35887 100644 --- a/fastpair/scanning/BUILD +++ b/fastpair/scanning/BUILD @@ -33,7 +33,6 @@ cc_library( "//fastpair/repository:device_repository", "//fastpair/scanning/fastpair:scanning", "//internal/base", - "//internal/platform:logging", "//internal/platform:types", "@com_google_absl//absl/functional:bind_front", ], diff --git a/fastpair/scanning/fastpair/BUILD b/fastpair/scanning/fastpair/BUILD index 3ece0245..f70cd686 100644 --- a/fastpair/scanning/fastpair/BUILD +++ b/fastpair/scanning/fastpair/BUILD @@ -41,7 +41,6 @@ cc_library( "//internal/base", "//internal/platform:base", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:types", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/functional:bind_front", diff --git a/fastpair/server_access/BUILD b/fastpair/server_access/BUILD index 6aeffa98..89bfffa7 100644 --- a/fastpair/server_access/BUILD +++ b/fastpair/server_access/BUILD @@ -36,7 +36,6 @@ cc_library( "//internal/auth:types", "//internal/base", "//internal/network:types", - "//internal/platform:logging", "//internal/platform:types", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", diff --git a/fastpair/ui/BUILD b/fastpair/ui/BUILD index 16f74cbc..83ae1108 100644 --- a/fastpair/ui/BUILD +++ b/fastpair/ui/BUILD @@ -37,7 +37,7 @@ cc_library( "//fastpair/common", "//fastpair/repository", "//internal/base", - "//internal/platform:logging", + "//internal/platform:types", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/strings", ], diff --git a/internal/data/BUILD b/internal/data/BUILD index 562703c4..8628ea53 100644 --- a/internal/data/BUILD +++ b/internal/data/BUILD @@ -15,7 +15,7 @@ cc_library( "memory_data_set.h", ], deps = [ - "//internal/platform:logging", + "//internal/platform:types", "//third_party/leveldb:db", "//third_party/leveldb:table", "//third_party/leveldb:util", diff --git a/internal/network/BUILD b/internal/network/BUILD index 634db625..9a1b24f5 100644 --- a/internal/network/BUILD +++ b/internal/network/BUILD @@ -55,7 +55,6 @@ cc_library( ], deps = [ ":types", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform/implementation:platform", "@com_google_absl//absl/base:core_headers", diff --git a/internal/platform/BUILD b/internal/platform/BUILD index fc4597a4..66056d0f 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -94,31 +94,6 @@ cc_library( ], ) -cc_library( - name = "logging", - hdrs = [ - "logging.h", - ], - copts = ["-DCORE_ADAPTER_DLL"], - visibility = [ - "//connections:__subpackages__", - "//fastpair:__subpackages__", - "//internal/auth:__subpackages__", - "//internal/auth/credential_store:__subpackages__", - "//internal/data:__subpackages__", - "//internal/network:__subpackages__", - "//internal/platform:__subpackages__", - "//internal/proto/analytics:__subpackages__", - "//location/nearby/analytics/cpp:__subpackages__", - "//presence:__subpackages__", - ], - deps = [ - "//internal/platform/implementation:platform", - "//internal/platform/implementation:types", - "@com_google_glog//:glog", - ], -) - cc_library( name = "cancellation_flag", srcs = [ @@ -161,7 +136,7 @@ cc_library( "//presence:__subpackages__", ], deps = [ - ":logging", + ":types", "//proto:connections_enums_cc_proto", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", @@ -182,7 +157,7 @@ cc_library( ], visibility = ["//connections/implementation:__subpackages__"], deps = [ - ":logging", + ":types", "//proto:connections_enums_cc_proto", "//proto/errorcode:error_code_enums_cc_proto", "@com_google_absl//absl/functional:any_invocable", @@ -227,7 +202,6 @@ cc_library( ], deps = [ ":base", - ":logging", ":types", ":uuid", "//internal/base", @@ -355,15 +329,21 @@ cc_library( "//connections:__subpackages__", "//fastpair:__subpackages__", "//internal/account:__subpackages__", + "//internal/auth:__subpackages__", + "//internal/auth/credential_store:__subpackages__", "//internal/base:__subpackages__", + "//internal/data:__subpackages__", "//internal/flags:__subpackages__", "//internal/interop:__pkg__", "//internal/network:__subpackages__", + "//internal/platform:__subpackages__", "//internal/platform/implementation/g3:__pkg__", "//internal/platform/implementation/windows:__subpackages__", "//internal/preferences:__subpackages__", + "//internal/proto/analytics:__subpackages__", "//internal/test:__subpackages__", "//internal/weave:__subpackages__", + "//location/nearby/analytics/cpp:__subpackages__", "//location/nearby/cpp:__subpackages__", "//location/nearby/testing/nearby_native:__subpackages__", "//presence:__subpackages__", @@ -371,7 +351,6 @@ cc_library( ], deps = [ ":base", - ":logging", ":util", "//internal/crypto_cros", "//internal/platform/implementation:platform", @@ -427,7 +406,6 @@ cc_library( deps = [ ":base", ":cancellation_flag", - ":logging", ":types", ":uuid", "//internal/base", @@ -488,7 +466,6 @@ cc_test( ":base", ":comm", ":connection_info", - ":logging", ":test_util", ":types", ":uuid", diff --git a/internal/platform/implementation/g3/BUILD b/internal/platform/implementation/g3/BUILD index b20cd459..5dd64cc6 100644 --- a/internal/platform/implementation/g3/BUILD +++ b/internal/platform/implementation/g3/BUILD @@ -44,8 +44,8 @@ cc_library( deps = [ ":preferences_repository", "//internal/platform:base", - "//internal/platform:logging", "//internal/platform:test_util", + "//internal/platform:types", "//internal/platform:util", "//internal/platform/implementation:types", "//internal/platform/implementation/shared:count_down_latch", @@ -103,7 +103,6 @@ cc_library( "@com_google_absl//absl/synchronization", "//internal/platform:base", "//internal/platform:cancellation_flag", - "//internal/platform:logging", "//internal/platform:test_util", "//internal/platform:types", "//internal/platform:uuid", diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index 9e4ac2f9..8f615664 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -50,7 +50,6 @@ cc_library( "//base:stringprintf", "//internal/base:bluetooth_address", "//internal/platform:base", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform:uuid", "//internal/platform/implementation:types", @@ -205,7 +204,6 @@ cc_library( "//internal/platform:base", "//internal/platform:cancellation_flag", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform:uuid", "//internal/platform/flags:platform_flags", @@ -286,7 +284,7 @@ cc_test( ":types", ":windows", "//internal/platform:base", - "//internal/platform:logging", + "//internal/platform:types", "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", "//internal/platform/implementation:types", diff --git a/internal/proto/analytics/BUILD b/internal/proto/analytics/BUILD index ea1b3b08..904d2182 100644 --- a/internal/proto/analytics/BUILD +++ b/internal/proto/analytics/BUILD @@ -64,7 +64,7 @@ cc_test( shard_count = 16, deps = [ ":connections_log_cc_proto", - "//internal/platform:logging", + "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep "//proto:connections_enums_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", diff --git a/presence/BUILD b/presence/BUILD index 9645c99c..c4b38578 100644 --- a/presence/BUILD +++ b/presence/BUILD @@ -91,7 +91,7 @@ cc_library( "//internal/interop:device", "//internal/platform:base", "//internal/platform:connection_info", - "//internal/platform:logging", + "//internal/platform:types", "//internal/platform/implementation:types", "//internal/proto:credential_cc_proto", "//internal/proto:metadata_cc_proto", @@ -122,7 +122,7 @@ cc_test( ":types", "//connections/implementation/proto:offline_wire_formats_cc_proto", "//internal/platform:connection_info", - "//internal/platform:logging", + "//internal/platform:types", "//internal/proto:credential_cc_proto", "//internal/proto:metadata_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", diff --git a/presence/fpp/BUILD b/presence/fpp/BUILD index 2f12acb7..5aa6744a 100644 --- a/presence/fpp/BUILD +++ b/presence/fpp/BUILD @@ -24,7 +24,7 @@ cc_library( "//presence:__subpackages__", ], deps = [ - "//internal/platform:logging", + "//internal/platform:types", "//presence:types", "//presence/fpp/fpp_c_ffi", "//presence/implementation:sensor_fusion", diff --git a/presence/implementation/BUILD b/presence/implementation/BUILD index 6f142289..febab2ed 100644 --- a/presence/implementation/BUILD +++ b/presence/implementation/BUILD @@ -87,7 +87,6 @@ cc_library( "//internal/crypto_cros", "//internal/platform:base", "//internal/platform:comm", - "//internal/platform:logging", "//internal/platform:types", "//internal/platform:uuid", "//internal/platform/implementation:comm", From e703b21cde63b303b41157dd7b71b80a7916025b Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Wed, 9 Aug 2023 14:50:30 -0700 Subject: [PATCH 071/128] Fix connections_test PiperOrigin-RevId: 555282324 --- .../c/bluetooth_classic_server_socket_test.cc | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/connections/c/bluetooth_classic_server_socket_test.cc b/connections/c/bluetooth_classic_server_socket_test.cc index 10cb1051..2b0e1394 100644 --- a/connections/c/bluetooth_classic_server_socket_test.cc +++ b/connections/c/bluetooth_classic_server_socket_test.cc @@ -117,10 +117,10 @@ TEST(BluetoothClassicServerSocketTest, PerformanceTimer::start(); core.StartAdvertising(SERVICE_ID, AdvertiseOptions, request_info, - {.result_cb = [&](Status status) { + [&](Status status) { request_result = status; notification.Notify(); - }}); + }); if (notification.WaitForNotificationWithTimeout( absl::Seconds(TimeoutSeconds))) { @@ -163,10 +163,10 @@ TEST(BluetoothClassicServerSocketTest, PerformanceTimer::start(); core.StartAdvertising(SERVICE_ID, AdvertiseOptions, request_info, - {.result_cb = [&](Status status) { + [&](Status status) { request_result = status; notification2.Notify(); - }}); + }); if (notification2.WaitForNotificationWithTimeout( absl::Seconds(TimeoutSeconds))) { @@ -209,10 +209,10 @@ TEST(BluetoothClassicServerSocketTest, PerformanceTimer::start(); - core.StopAdvertising({.result_cb = [&](Status status) { + core.StopAdvertising([&](Status status) { request_result = status; notification3.Notify(); - }}); + }); if (notification3.WaitForNotificationWithTimeout( absl::Seconds(TimeoutSeconds))) { @@ -273,10 +273,10 @@ TEST(BluetoothClassicServerSocketTest, DISABLED_MultiRunWithTimeoutReproStuck) { PerformanceTimer::start(); core.StartAdvertising(SERVICE_ID, AdvertiseOptions, request_info, - {.result_cb = [&](Status status) { + [&](Status status) { request_result = status; notification.Notify(); - }}); + }); if (notification.WaitForNotificationWithTimeout( absl::Seconds(TimeoutSeconds))) { @@ -319,10 +319,10 @@ TEST(BluetoothClassicServerSocketTest, DISABLED_MultiRunWithTimeoutReproStuck) { PerformanceTimer::start(); core.StartAdvertising(SERVICE_ID, AdvertiseOptions, request_info, - {.result_cb = [&](Status status) { + [&](Status status) { request_result = status; notification2.Notify(); - }}); + }); if (notification2.WaitForNotificationWithTimeout( absl::Seconds(TimeoutSeconds))) { @@ -361,10 +361,10 @@ TEST(BluetoothClassicServerSocketTest, DISABLED_MultiRunWithTimeoutReproStuck) { PerformanceTimer::start(); - core.StopAdvertising({.result_cb = [&](Status status) { + core.StopAdvertising([&](Status status) { request_result = status; notification3.Notify(); - }}); + }); if (notification3.WaitForNotificationWithTimeout( absl::Seconds(TimeoutSeconds))) { @@ -424,10 +424,10 @@ TEST(BluetoothClassicServerSocketTest, DISABLED_SingleRunNoTimeoutReproStuck) { PerformanceTimer::start(); core.StartAdvertising(SERVICE_ID, AdvertiseOptions, request_info, - {.result_cb = [&](Status status) { + [&](Status status) { request_result = status; notification.Notify(); - }}); + }); notification.WaitForNotification(); PerformanceTimer::stop(); @@ -451,10 +451,10 @@ TEST(BluetoothClassicServerSocketTest, DISABLED_SingleRunNoTimeoutReproStuck) { PerformanceTimer::start(); core.StartAdvertising(SERVICE_ID, AdvertiseOptions, request_info, - {.result_cb = [&](Status status) { + [&](Status status) { request_result = status; notification2.Notify(); - }}); + }); notification2.WaitForNotification(); PerformanceTimer::stop(); @@ -477,10 +477,10 @@ TEST(BluetoothClassicServerSocketTest, DISABLED_SingleRunNoTimeoutReproStuck) { PerformanceTimer::start(); - core.StopAdvertising({.result_cb = [&](Status status) { + core.StopAdvertising([&](Status status) { request_result = status; notification3.Notify(); - }}); + }); notification3.WaitForNotification(); PerformanceTimer::stop(); @@ -521,10 +521,10 @@ TEST(BluetoothClassicServerSocketTest, DISABLED_MultiRunNoTimeoutReproStuck) { PerformanceTimer::start(); core.StartAdvertising(SERVICE_ID, AdvertiseOptions, request_info, - {.result_cb = [&](Status status) { + [&](Status status) { request_result = status; notification.Notify(); - }}); + }); notification.WaitForNotification(); @@ -551,10 +551,10 @@ TEST(BluetoothClassicServerSocketTest, DISABLED_MultiRunNoTimeoutReproStuck) { PerformanceTimer::start(); core.StartAdvertising(SERVICE_ID, AdvertiseOptions, request_info, - {.result_cb = [&](Status status) { + [&](Status status) { request_result = status; notification2.Notify(); - }}); + }); notification2.WaitForNotification(); PerformanceTimer::stop(); @@ -580,10 +580,10 @@ TEST(BluetoothClassicServerSocketTest, DISABLED_MultiRunNoTimeoutReproStuck) { PerformanceTimer::start(); - core.StopAdvertising({.result_cb = [&](Status status) { + core.StopAdvertising([&](Status status) { request_result = status; notification3.Notify(); - }}); + }); notification3.WaitForNotification(); PerformanceTimer::stop(); From 5589166b3bd70be136d50acdce40f930f082993f Mon Sep 17 00:00:00 2001 From: Suet-Fei Li Date: Wed, 9 Aug 2023 19:03:40 -0700 Subject: [PATCH 072/128] Update copyright year. PiperOrigin-RevId: 555341312 --- connections/advertising_options.cc | 2 +- connections/advertising_options.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/connections/advertising_options.cc b/connections/advertising_options.cc index 1874badb..2355175f 100644 --- a/connections/advertising_options.cc +++ b/connections/advertising_options.cc @@ -1,4 +1,4 @@ -// Copyright 2021 Google LLC +// 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. diff --git a/connections/advertising_options.h b/connections/advertising_options.h index d0fbf7f3..1f87960f 100644 --- a/connections/advertising_options.h +++ b/connections/advertising_options.h @@ -1,4 +1,4 @@ -// Copyright 2021 Google LLC +// 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. From cce7cf59b95ff6db2d30462be175666e57627ebc Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Thu, 10 Aug 2023 14:16:19 -0700 Subject: [PATCH 073/128] Replace std::function with AnyInvocable PiperOrigin-RevId: 555650115 --- connections/implementation/mediums/BUILD | 1 + connections/implementation/mediums/webrtc.cc | 7 +- connections/implementation/mediums/webrtc.h | 13 ++-- .../implementation/mediums/webrtc_test.cc | 66 +++++++++---------- .../implementation/webrtc_bwu_handler.cc | 11 ++-- 5 files changed, 47 insertions(+), 51 deletions(-) diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD index 2ef5be09..554b8288 100644 --- a/connections/implementation/mediums/BUILD +++ b/connections/implementation/mediums/BUILD @@ -62,6 +62,7 @@ cc_library( "@com_google_absl//absl/container:btree", "@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/functional:bind_front", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", diff --git a/connections/implementation/mediums/webrtc.cc b/connections/implementation/mediums/webrtc.cc index e02a81c2..21ad6e8c 100644 --- a/connections/implementation/mediums/webrtc.cc +++ b/connections/implementation/mediums/webrtc.cc @@ -107,7 +107,7 @@ bool WebRtc::StartAcceptingConnections(const std::string& service_id, // who may be also using WebRTC. AcceptingConnectionsInfo info = AcceptingConnectionsInfo(); info.self_peer_id = self_peer_id; - info.accepted_connection_callback = callback; + info.accepted_connection_callback = std::move(callback); // Create a new SignalingMessenger so that we can communicate w/ Tachyon. info.signaling_messenger = @@ -688,8 +688,9 @@ void WebRtc::ProcessDataChannelOpen(const std::string& service_id, 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.accepted_cb( + 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; } diff --git a/connections/implementation/mediums/webrtc.h b/connections/implementation/mediums/webrtc.h index 091cafb1..8e8eef39 100644 --- a/connections/implementation/mediums/webrtc.h +++ b/connections/implementation/mediums/webrtc.h @@ -17,13 +17,12 @@ #ifndef NO_WEBRTC -#include -#include #include #include #include #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_peer_id.h" #include "connections/implementation/mediums/webrtc_socket.h" @@ -42,15 +41,13 @@ namespace nearby { namespace connections { namespace mediums { -// Callback that is invoked when a new connection is accepted. -struct AcceptedConnectionCallback { - std::function - accepted_cb = [](const std::string&, WebRtcSocketWrapper) {}; -}; - // 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(); diff --git a/connections/implementation/mediums/webrtc_test.cc b/connections/implementation/mediums/webrtc_test.cc index 3b6bb0ca..3ab47b00 100644 --- a/connections/implementation/mediums/webrtc_test.cc +++ b/connections/implementation/mediums/webrtc_test.cc @@ -80,11 +80,11 @@ TEST_P(WebRtcTest, ConnectBothDevices_ShutdownSignaling_SendData) { receiver.StartAcceptingConnections( service_id, self_id, location_hint, - {[&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { + [&receiver_socket, connected](const std::string& service_id, + WebRtcSocketWrapper wrapper) mutable { receiver_socket = wrapper; connected.Set(receiver_socket.IsValid()); - }}); + }); CancellationFlag flag; sender_socket = sender.Connect(service_id, self_id, location_hint, &flag); @@ -119,11 +119,11 @@ TEST_P(WebRtcTest, CanCancelConnect) { receiver.StartAcceptingConnections( service_id, self_id, location_hint, - {[&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { + [&receiver_socket, connected](const std::string& service_id, + WebRtcSocketWrapper wrapper) mutable { receiver_socket = wrapper; connected.Set(receiver_socket.IsValid()); - }}); + }); CancellationFlag flag(true); sender_socket = sender.Connect(service_id, self_id, location_hint, &flag); @@ -173,10 +173,10 @@ TEST_F(WebRtcTest, StartAcceptingConnectionTwice) { ASSERT_TRUE(webrtc.IsAvailable()); ASSERT_TRUE(webrtc.StartAcceptingConnections( service_id, self_id, location_hint, - {mock_accepted_callback_.AsStdFunction()})); + mock_accepted_callback_.AsStdFunction())); EXPECT_FALSE(webrtc.StartAcceptingConnections( service_id, self_id, location_hint, - {mock_accepted_callback_.AsStdFunction()})); + mock_accepted_callback_.AsStdFunction())); EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id)); EXPECT_FALSE(webrtc.IsAcceptingConnections(std::string{})); env_.Stop(); @@ -197,8 +197,8 @@ TEST_F(WebRtcTest, Connect_NoPeer) { webrtc.Connect(service_id, peer_id, location_hint, &flag); EXPECT_FALSE(wrapper_1.IsValid()); - EXPECT_TRUE(webrtc.StartAcceptingConnections( - service_id, peer_id, location_hint, AcceptedConnectionCallback())); + EXPECT_TRUE(webrtc.StartAcceptingConnections(service_id, peer_id, + location_hint, nullptr)); env_.Stop(); } @@ -215,7 +215,7 @@ TEST_F(WebRtcTest, StartAcceptingConnection_ThenConnect) { ASSERT_TRUE(webrtc.IsAvailable()); ASSERT_TRUE(webrtc.StartAcceptingConnections( service_id, self_id, location_hint, - {mock_accepted_callback_.AsStdFunction()})); + mock_accepted_callback_.AsStdFunction())); CancellationFlag flag; WebRtcSocketWrapper wrapper = webrtc.Connect( service_id, WebrtcPeerId("random_peer_id"), location_hint, &flag); @@ -223,7 +223,7 @@ TEST_F(WebRtcTest, StartAcceptingConnection_ThenConnect) { EXPECT_FALSE(wrapper.IsValid()); EXPECT_FALSE(webrtc.StartAcceptingConnections( service_id, self_id, location_hint, - {mock_accepted_callback_.AsStdFunction()})); + mock_accepted_callback_.AsStdFunction())); env_.Stop(); } @@ -240,7 +240,7 @@ TEST_F(WebRtcTest, StartAndStopAcceptingConnections) { ASSERT_TRUE(webrtc.IsAvailable()); ASSERT_TRUE(webrtc.StartAcceptingConnections( service_id, self_id, location_hint, - {mock_accepted_callback_.AsStdFunction()})); + mock_accepted_callback_.AsStdFunction())); EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id)); webrtc.StopAcceptingConnections(service_id); EXPECT_FALSE(webrtc.IsAcceptingConnections(service_id)); @@ -261,15 +261,15 @@ TEST_F(WebRtcTest, ConnectTwice) { receiver.StartAcceptingConnections( service_id, self_id, location_hint, - {[&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { + [&receiver_socket, connected](const std::string& service_id, + WebRtcSocketWrapper wrapper) mutable { receiver_socket = wrapper; connected.Set(receiver_socket.IsValid()); - }}); + }); device_c.StartAcceptingConnections( service_id, other_id, location_hint, - {[](const std::string& service_id, WebRtcSocketWrapper wrapper) {}}); + [](const std::string& service_id, WebRtcSocketWrapper wrapper) {}); CancellationFlag flag; sender_socket = sender.Connect(service_id, self_id, location_hint, &flag); @@ -311,11 +311,11 @@ TEST_F(WebRtcTest, ConnectBothDevicesAndAbort) { receiver.StartAcceptingConnections( service_id, self_id, location_hint, - {[&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { + [&receiver_socket, connected](const std::string& service_id, + WebRtcSocketWrapper wrapper) mutable { receiver_socket = wrapper; connected.Set(receiver_socket.IsValid()); - }}); + }); CancellationFlag flag; sender_socket = sender.Connect(service_id, self_id, location_hint, &flag); @@ -343,11 +343,11 @@ TEST_F(WebRtcTest, ConnectBothDevicesAndSendData) { receiver.StartAcceptingConnections( service_id, self_id, location_hint, - {[&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { + [&receiver_socket, connected](const std::string& service_id, + WebRtcSocketWrapper wrapper) mutable { receiver_socket = wrapper; connected.Set(receiver_socket.IsValid()); - }}); + }); CancellationFlag flag; sender_socket = sender.Connect(service_id, self_id, location_hint, &flag); @@ -399,7 +399,7 @@ TEST_F(WebRtcTest, ContinueAcceptingConnectionsOnComplete) { ASSERT_TRUE(webrtc.IsAvailable()); ASSERT_TRUE(webrtc.StartAcceptingConnections( service_id, self_id, location_hint, - {mock_accepted_callback_.AsStdFunction()})); + mock_accepted_callback_.AsStdFunction())); EXPECT_TRUE(webrtc.IsAcceptingConnections(service_id)); // Simulate a failure in receiving messages stream, WebRtc should restart @@ -450,11 +450,11 @@ TEST_F(WebRtcTest, CancelDuringConnect) { receiver->StartAcceptingConnections( service_id, self_id, location_hint, - {[&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { + [&receiver_socket, connected](const std::string& service_id, + WebRtcSocketWrapper wrapper) mutable { receiver_socket = wrapper; connected.Set(receiver_socket.IsValid()); - }}); + }); sender_socket = sender->Connect(service_id, self_id, location_hint, &sender_flag); @@ -496,11 +496,11 @@ TEST_F(WebRtcTest, CancelBeforeConnect) { receiver->StartAcceptingConnections( service_id, self_id, location_hint, - {[&receiver_socket, connected](const std::string& service_id, - WebRtcSocketWrapper wrapper) mutable { + [&receiver_socket, connected](const std::string& service_id, + WebRtcSocketWrapper wrapper) mutable { receiver_socket = wrapper; connected.Set(receiver_socket.IsValid()); - }}); + }); sender_socket = sender->Connect(service_id, self_id, location_hint, &sender_flag); @@ -541,11 +541,11 @@ TEST_F(WebRtcTest, CancelDuringConnect_MultipleConnect) { receiver->StartAcceptingConnections( ns_service_id, self_id, location_hint, - {[&receiver_socket, connected](const std::string& ns_service_id, - WebRtcSocketWrapper wrapper) mutable { + [&receiver_socket, connected](const std::string& ns_service_id, + WebRtcSocketWrapper wrapper) mutable { receiver_socket = wrapper; connected.Set(receiver_socket.IsValid()); - }}); + }); // Simulate a successful connect for the endpoint of NearbySharing. sender_socket = sender->Connect(ns_service_id, self_id, location_hint, &flag); diff --git a/connections/implementation/webrtc_bwu_handler.cc b/connections/implementation/webrtc_bwu_handler.cc index 535d2bc1..1941abca 100644 --- a/connections/implementation/webrtc_bwu_handler.cc +++ b/connections/implementation/webrtc_bwu_handler.cc @@ -20,6 +20,7 @@ #include #include "absl/functional/bind_front.h" +#include "connections/implementation/base_bwu_handler.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/mediums/utils.h" #include "connections/implementation/mediums/webrtc_peer_id.h" @@ -41,8 +42,7 @@ std::string WebrtcBwuHandler::WebrtcIncomingSocket::ToString() { return name_; } WebrtcBwuHandler::WebrtcBwuHandler(Mediums& mediums, BwuNotifications notifications) - : BaseBwuHandler(std::move(notifications)), - mediums_(mediums) {} + : BaseBwuHandler(std::move(notifications)), mediums_(mediums) {} // Called by BWU target. Retrieves a new medium info from incoming message, // and establishes connection over WebRTC using this info. @@ -114,11 +114,8 @@ ByteArray WebrtcBwuHandler::HandleInitializeUpgradedMediumForEndpoint( if (!webrtc_.IsAcceptingConnections(upgrade_service_id)) { if (!webrtc_.StartAcceptingConnections( upgrade_service_id, self_id, location_hint, - { - .accepted_cb = absl::bind_front( - &WebrtcBwuHandler::OnIncomingWebrtcConnection, this, - client), - })) { + absl::bind_front(&WebrtcBwuHandler::OnIncomingWebrtcConnection, + this, client))) { NEARBY_LOG(ERROR, "WebRtcBwuHandler couldn't initiate the WEB_RTC upgrade for " "endpoint %s because it failed to start listening for " From 85bf78ef70dc89cffc03bb0c2d3a99e05451c2cd Mon Sep 17 00:00:00 2001 From: Joy Babafemi Date: Thu, 10 Aug 2023 15:23:16 -0700 Subject: [PATCH 074/128] Add sensor fusion implementation to presence C++ PiperOrigin-RevId: 555676715 --- presence/fpp/BUILD | 37 +++++++- presence/fpp/fpp_c_ffi/src/lib.rs | 46 +++++----- presence/fpp/fpp_manager.cc | 58 +++++++------ presence/fpp/fpp_manager.h | 5 ++ presence/fpp/fpp_manager_test.cc | 74 +++++++++++++--- presence/fpp/sensor_fusion_impl.cc | 68 +++++++++++++++ presence/fpp/sensor_fusion_impl.h | 51 +++++++++++ presence/fpp/sensor_fusion_test.cc | 110 ++++++++++++++++++++++++ presence/implementation/BUILD | 1 + presence/implementation/sensor_fusion.h | 23 ++--- 10 files changed, 400 insertions(+), 73 deletions(-) create mode 100644 presence/fpp/sensor_fusion_impl.cc create mode 100644 presence/fpp/sensor_fusion_impl.h create mode 100644 presence/fpp/sensor_fusion_test.cc diff --git a/presence/fpp/BUILD b/presence/fpp/BUILD index 5aa6744a..931c75a2 100644 --- a/presence/fpp/BUILD +++ b/presence/fpp/BUILD @@ -30,7 +30,22 @@ cc_library( "//presence/implementation:sensor_fusion", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/status", - "@com_google_absl//absl/types:optional", + ], +) + +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", ], ) @@ -53,3 +68,23 @@ cc_test( ], }), ) + +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_c_ffi/src/lib.rs b/presence/fpp/fpp_c_ffi/src/lib.rs index 6300a64b..b0c536f7 100644 --- a/presence/fpp/fpp_c_ffi/src/lib.rs +++ b/presence/fpp/fpp_c_ffi/src/lib.rs @@ -35,29 +35,31 @@ pub struct PresenceDetectorHandle { handle: u64, } -/// Error enum class representing possible errors +/// Enum class representing possible outputs of proximity data processing call #[repr(C)] -pub enum ProximityEstimateError { - /// Returned if the handle is invalid - InvalidPresenceDetectorHandle, - /// Returned if the output parameter is null - NullOutputParameter, +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 ProximityEstimateError { - fn to_error_code(&self) -> i32 { +impl ComputationStatus { + fn to_status_code(&self) -> i32 { match self { - Self::InvalidPresenceDetectorHandle => -1, - Self::NullOutputParameter => -2, - Self::NoComputedProximityEstimate => -3, + /// Status codes 100+ are considered errors + Self::Success => 1, + Self::NoComputedProximityEstimate => 2, + Self::InvalidPresenceDetectorHandleError => 101, + Self::NullOutputParameterError => 102, } } } -const SUCCESS: i32 = 0; - /// Creates a new presence detector object and returns the handle for the new /// object #[no_mangle] @@ -86,15 +88,15 @@ pub unsafe extern "C" fn update_ble_scan_result( { if let Some(proximity_estimate) = proximity_estimate.as_mut() { *proximity_estimate = current_proximity_estimate; - SUCCESS + ComputationStatus::Success.to_status_code() } else { - ProximityEstimateError::NullOutputParameter.to_error_code() + ComputationStatus::NullOutputParameterError.to_status_code() } } else { - ProximityEstimateError::NoComputedProximityEstimate.to_error_code() + ComputationStatus::NoComputedProximityEstimate.to_status_code() } } else { - ProximityEstimateError::InvalidPresenceDetectorHandle.to_error_code() + ComputationStatus::InvalidPresenceDetectorHandleError.to_status_code() } } @@ -115,13 +117,13 @@ pub unsafe extern "C" fn get_proximity_estimate( 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 SUCCESS; + return ComputationStatus::Success.to_status_code(); } - ProximityEstimateError::NullOutputParameter.to_error_code() + ComputationStatus::NullOutputParameterError.to_status_code() }); } - ProximityEstimateError::InvalidPresenceDetectorHandle.to_error_code() + ComputationStatus::InvalidPresenceDetectorHandleError.to_status_code() } /// De-allocates memory for a presence detector object @@ -133,7 +135,7 @@ pub extern "C" fn presence_detector_free( get_presence_detector_handle_map().remove(&presence_detector_handle.handle) { let _ = *presence_detector; - return SUCCESS; + return ComputationStatus::Success.to_status_code(); } - ProximityEstimateError::InvalidPresenceDetectorHandle.to_error_code() + ComputationStatus::InvalidPresenceDetectorHandleError.to_status_code() } diff --git a/presence/fpp/fpp_manager.cc b/presence/fpp/fpp_manager.cc index f0ce1ec2..dfe2f4d4 100644 --- a/presence/fpp/fpp_manager.cc +++ b/presence/fpp/fpp_manager.cc @@ -29,30 +29,22 @@ namespace nearby { namespace presence { namespace { -constexpr int kSuccess = 0; -constexpr int kInvalidPresenceDetectorHandle = -1; -constexpr int kNullOutputParameter = -2; -constexpr int kNoComputedProximityEstimate = -3; +// 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(absl::optional txPower) { +MaybeTxPower ConvertTxPower(std::optional txPower) { if (txPower.has_value()) { return {MaybeTxPower::Tag::Valid, {txPower.value()}}; } return {MaybeTxPower::Tag::Invalid, {}}; } -std::string GetStatusStringFromCode(int status_code) { - switch (status_code) { - case kInvalidPresenceDetectorHandle: - return "INVALID_PRESENCE_DETECTOR_HANDLE"; - case kNullOutputParameter: - return "NULL_OUTPUT_PARAMETER"; - default: - return "UNKNOWN_ERROR"; - } -} - // Converts FPP ProximityState struct to NP RangeType struct PresenceZone::DistanceBoundary::RangeType ConvertProximityStateToRangeType( ProximityState proximity_state) { @@ -67,6 +59,7 @@ PresenceZone::DistanceBoundary::RangeType ConvertProximityStateToRangeType( return PresenceZone::DistanceBoundary::RangeType::kFar; case ProximityState::Unknown: default: + NEARBY_LOGS(WARNING) << "Proximity state is unknown"; return PresenceZone::DistanceBoundary::RangeType::kRangeUnknown; } } @@ -76,13 +69,16 @@ 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, - 0.0, + /*distanceMeters=*/0.0, MeasurementConfidence::Unknown, - 0, + /*elapsedRealtime=*/0, ProximityState::Unknown, PresenceDataSource::Ble}; ProximityEstimate old_proximity_estimate = @@ -106,8 +102,7 @@ absl::Status FppManager::UpdateBleScanResult(uint64_t device_id, NEARBY_LOGS(WARNING) << "Could not successfully update FPP with new scan result: Error code=" << status_code; - return absl::Status(absl::StatusCode::kInternal, - GetStatusStringFromCode(status_code)); + return absl::InternalError(GetStatusStringFromCode(status_code)); } void FppManager::RegisterZoneTransitionListener( @@ -132,12 +127,13 @@ std::optional FppManager::GetRangingData(uint64_t device_id) { RangingData FppManager::ConvertProximityEstimateToRangingData( ProximityEstimate estimate) { RangingMeasurement ranging_measurement = { - 0.0, static_cast(estimate.distance_meters)}; - RangingPosition ranging_position = {ranging_measurement, absl::nullopt, - absl::nullopt, - estimate.elapsed_real_time_millis}; + /*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), 0.0}; + ConvertProximityStateToRangeType(estimate.proximity_state), + /*confidenceLevel=*/0.0}; return {DataSource::kBle, ranging_position, zone_transition, std::vector()}; } @@ -157,5 +153,17 @@ void FppManager::CheckPresenceZoneChanged(uint64_t device_id, } } +std::string FppManager::GetStatusStringFromCode(int status_code) { + switch (status_code) { + case kInvalidPresenceDetectorHandleError: + return "INVALID_PRESENCE_DETECTOR_HANDLE"; + case kNullOutputParameterError: + return "NULL_OUTPUT_PARAMETER"; + default: + NEARBY_LOGS(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 index d13e4adb..cecdb21e 100644 --- a/presence/fpp/fpp_manager.h +++ b/presence/fpp/fpp_manager.h @@ -66,6 +66,11 @@ class FppManager { */ 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, diff --git a/presence/fpp/fpp_manager_test.cc b/presence/fpp/fpp_manager_test.cc index 76dc2fc2..6911e06f 100644 --- a/presence/fpp/fpp_manager_test.cc +++ b/presence/fpp/fpp_manager_test.cc @@ -19,6 +19,7 @@ #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 { @@ -40,11 +41,11 @@ TEST(FppManager, UpdateBleScanResultSuccess) { PresenceZone::DistanceBoundary::RangeType range_type) { callback_called = true; }}); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, + 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=*/absl::nullopt, + EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, kReachRssi, /*elapsed_real_time_millis=*/2000)); EXPECT_EQ(manager.GetRangingData(kDeviceId) @@ -66,10 +67,10 @@ TEST(FppManager, ZoneTransitionDetected) { callback_called = true; }}); // ProximityEstimate is only computed after consecutive scans is fulfilled - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, + EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, kReachRssi, /*elapsed_real_time_millis=*/0)); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, + EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, kReachRssi, /*elapsed_real_time_millis=*/2000)); EXPECT_EQ(manager.GetRangingData(kDeviceId) @@ -80,7 +81,7 @@ TEST(FppManager, ZoneTransitionDetected) { callback_called = false; // Update with new zone - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, + EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, kShortRangeRssi, /*elapsed_real_time_millis=*/0)); EXPECT_EQ(manager.GetRangingData(kDeviceId) @@ -89,7 +90,7 @@ TEST(FppManager, ZoneTransitionDetected) { PresenceZone::DistanceBoundary::RangeType::kWithinReach); EXPECT_FALSE(callback_called); // Update with consecutive scan of new zone - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, + EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, kShortRangeRssi, /*elapsed_real_time_millis=*/0)); EXPECT_EQ(manager.GetRangingData(kDeviceId) @@ -172,10 +173,10 @@ TEST(FppManager, UnregisterZoneTransitionListener) { PresenceZone::DistanceBoundary::RangeType range_type) { callback_called = true; }}); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, + EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, kReachRssi, /*elapsed_real_time_millis=*/0)); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, + EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/std::nullopt, kReachRssi, /*elapsed_real_time_millis=*/2000)); EXPECT_TRUE(callback_called); @@ -183,15 +184,60 @@ TEST(FppManager, UnregisterZoneTransitionListener) { // Unregister listener and update with new zone manager.UnregisterZoneTransitionListener(kCallbackId); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, - kShortRangeRssi, - /*elapsed_real_time_millis=*/0)); - EXPECT_OK(manager.UpdateBleScanResult(kDeviceId, /*txPower=*/absl::nullopt, - kShortRangeRssi, - /*elapsed_real_time_millis=*/0)); + 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 new file mode 100644 index 00000000..2bda18b5 --- /dev/null +++ b/presence/fpp/sensor_fusion_impl.cc @@ -0,0 +1,68 @@ +// 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 new file mode 100644 index 00000000..ac37b6a8 --- /dev/null +++ b/presence/fpp/sensor_fusion_impl.h @@ -0,0 +1,51 @@ +// 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 new file mode 100644 index 00000000..aee34d7b --- /dev/null +++ b/presence/fpp/sensor_fusion_test.cc @@ -0,0 +1,110 @@ +// 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 index febab2ed..a7b9de53 100644 --- a/presence/implementation/BUILD +++ b/presence/implementation/BUILD @@ -126,6 +126,7 @@ cc_library( deps = [ "//presence:types", "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/status", ], ) diff --git a/presence/implementation/sensor_fusion.h b/presence/implementation/sensor_fusion.h index facec76e..31639bd7 100644 --- a/presence/implementation/sensor_fusion.h +++ b/presence/implementation/sensor_fusion.h @@ -21,6 +21,7 @@ #include #include "absl/functional/any_invocable.h" +#include "absl/status/status.h" #include "presence/device_motion.h" #include "presence/presence_zone.h" @@ -92,7 +93,7 @@ class SensorFusion { */ virtual std::vector GetDataSources( uint64_t elapsed_realtime_millis, - const std::vector& available_sources); + const std::vector& available_sources) = 0; /** * Updates BLE scanned results to Sensor Fusion. @@ -104,10 +105,9 @@ class SensorFusion { * @param elapsed_realtime_millis Elapsed timestamp since boot when the * scan result is discovered. */ - virtual void UpdateBleScanResult(uint64_t device_id, - std::optional txPower, int rssi, - uint64_t elapsed_realtime_millis); - + 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. * @@ -115,27 +115,28 @@ class SensorFusion { * @param position UWB ranging result (distance and optionally angle) */ virtual void UpdateUwbRangingResult(uint64_t device_id, - RangingPosition position); + RangingPosition position) = 0; /** * Adds callback for updates of proximity zone transitions. */ - virtual void RequestZoneTransitionUpdates(ZoneTransitionCallback callback); + virtual void RequestZoneTransitionUpdates( + ZoneTransitionCallback callback) = 0; /** * Removes callback for updates of proximity zone transitions. */ - virtual void RemoveZoneTransitionUpdates(ZoneTransitionCallback callback); + virtual void RemoveZoneTransitionUpdates(uint64_t callback_id) = 0; /** * Adds callback for updates of device motion events. */ - virtual void RequestDeviceMotionUpdates(DeviceMotionCallback callback); + virtual void RequestDeviceMotionUpdates(DeviceMotionCallback callback) = 0; /** * Remove callback for updates of device motion events. */ - virtual void RemoveDeviceMotionUpdates(DeviceMotionCallback callback); + virtual void RemoveDeviceMotionUpdates(DeviceMotionCallback callback) = 0; /** * Returns the best ranging estimate to a given device. Returns {@code @@ -143,7 +144,7 @@ class SensorFusion { * * @param device_id Id of the peer device. */ - virtual std::optional GetRangingData(uint64_t device_id); + virtual std::optional GetRangingData(uint64_t device_id) = 0; }; } // namespace presence From 8ccb9fcfec1b7fbc70462e96aab2de99de55f1a7 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Fri, 11 Aug 2023 14:26:25 -0700 Subject: [PATCH 075/128] Replace std::function with AnyInvocable PiperOrigin-RevId: 556090653 --- .../implementation/base_bwu_handler.cc | 14 +++++- connections/implementation/base_bwu_handler.h | 9 +++- .../implementation/base_bwu_handler_test.cc | 2 +- .../implementation/bluetooth_bwu_handler.cc | 9 ++-- .../implementation/bluetooth_bwu_handler.h | 5 +- connections/implementation/bwu_handler.h | 10 ++-- connections/implementation/bwu_manager.cc | 33 +++++++----- connections/implementation/fake_bwu_handler.h | 2 +- .../implementation/webrtc_bwu_handler.cc | 9 ++-- .../implementation/webrtc_bwu_handler.h | 4 +- .../implementation/webrtc_bwu_handler_stub.cc | 6 +-- .../implementation/webrtc_bwu_handler_stub.h | 4 +- .../implementation/wifi_direct_bwu_handler.cc | 9 ++-- .../implementation/wifi_direct_bwu_handler.h | 11 ++-- .../implementation/wifi_direct_bwu_test.cc | 40 ++++++--------- .../wifi_hotspot_bwu_handler.cc | 9 ++-- .../implementation/wifi_hotspot_bwu_handler.h | 11 ++-- .../implementation/wifi_hotspot_test.cc | 50 ++++++++----------- .../implementation/wifi_lan_bwu_handler.cc | 9 ++-- .../implementation/wifi_lan_bwu_handler.h | 4 +- 20 files changed, 134 insertions(+), 116 deletions(-) diff --git a/connections/implementation/base_bwu_handler.cc b/connections/implementation/base_bwu_handler.cc index c652dfbf..272e100d 100644 --- a/connections/implementation/base_bwu_handler.cc +++ b/connections/implementation/base_bwu_handler.cc @@ -23,8 +23,9 @@ namespace nearby { namespace connections { -BaseBwuHandler::BaseBwuHandler(BwuNotifications bwu_notifications) - : bwu_notifications_(std::move(bwu_notifications)) {} +BaseBwuHandler::BaseBwuHandler( + IncomingConnectionCallback incoming_connection_callback) + : incoming_connection_callback_(std::move(incoming_connection_callback)) {} ByteArray BaseBwuHandler::InitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& service_id, @@ -78,5 +79,14 @@ void BaseBwuHandler::RevertResponderState(const std::string& service_id) { HandleRevertInitiatorStateForService(service_id); } +void BaseBwuHandler::NotifyOnIncomingConnection( + ClientProxy* client, std::unique_ptr connection) { + if (!incoming_connection_callback_) { + NEARBY_LOGS(WARNING) + << "Ignoring incoming connection, no callback registered"; + return; + } + incoming_connection_callback_(client, std::move(connection)); +} } // namespace connections } // namespace nearby diff --git a/connections/implementation/base_bwu_handler.h b/connections/implementation/base_bwu_handler.h index a6d93625..783d90e4 100644 --- a/connections/implementation/base_bwu_handler.h +++ b/connections/implementation/base_bwu_handler.h @@ -30,7 +30,8 @@ namespace connections { // of the service IDs and endpoint IDs that initiated a bandwidth upgrade. class BaseBwuHandler : public BwuHandler { public: - explicit BaseBwuHandler(BwuNotifications bwu_notifications); + explicit BaseBwuHandler( + IncomingConnectionCallback incoming_connection_callback); // BwuHandler implementation: ByteArray InitializeUpgradedMediumForEndpoint( @@ -55,9 +56,13 @@ class BaseBwuHandler : public BwuHandler { virtual void HandleRevertInitiatorStateForService( const std::string& upgrade_service_id) = 0; - BwuNotifications bwu_notifications_; + // Notifies the caller about incoming connection. + void NotifyOnIncomingConnection( + ClientProxy* client, + std::unique_ptr connection); private: + IncomingConnectionCallback incoming_connection_callback_; // Map from the (wrapped) service ID to endpoint IDs that are initiating a // bandwidth upgrade. Not used for endpoints that respond to bandwidth upgrade // requests from another device. diff --git a/connections/implementation/base_bwu_handler_test.cc b/connections/implementation/base_bwu_handler_test.cc index d04cff92..13fe331b 100644 --- a/connections/implementation/base_bwu_handler_test.cc +++ b/connections/implementation/base_bwu_handler_test.cc @@ -38,7 +38,7 @@ class BwuHandlerImpl : public BaseBwuHandler { absl::optional endpoint_id; }; - BwuHandlerImpl() : BaseBwuHandler(BwuNotifications{}) {} + BwuHandlerImpl() : BaseBwuHandler(nullptr) {} const std::vector& handle_initialize_calls() const { return handle_initialize_calls_; diff --git a/connections/implementation/bluetooth_bwu_handler.cc b/connections/implementation/bluetooth_bwu_handler.cc index 894740d0..f60d607b 100644 --- a/connections/implementation/bluetooth_bwu_handler.cc +++ b/connections/implementation/bluetooth_bwu_handler.cc @@ -28,9 +28,10 @@ namespace nearby { namespace connections { -BluetoothBwuHandler::BluetoothBwuHandler(Mediums& mediums, - BwuNotifications notifications) - : BaseBwuHandler(std::move(notifications)), mediums_(mediums) {} +BluetoothBwuHandler::BluetoothBwuHandler( + 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 BT using this info. @@ -153,7 +154,7 @@ void BluetoothBwuHandler::OnIncomingBluetoothConnection( upgrade_service_id, socket), .channel = std::move(channel), }}; - bwu_notifications_.incoming_connection_cb(client, std::move(connection)); + NotifyOnIncomingConnection(client, std::move(connection)); } } // namespace connections diff --git a/connections/implementation/bluetooth_bwu_handler.h b/connections/implementation/bluetooth_bwu_handler.h index 2fa4d09b..6563eebf 100644 --- a/connections/implementation/bluetooth_bwu_handler.h +++ b/connections/implementation/bluetooth_bwu_handler.h @@ -31,8 +31,9 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class BluetoothBwuHandler : public BaseBwuHandler { public: - explicit BluetoothBwuHandler(Mediums& mediums, - BwuNotifications notifications); + explicit BluetoothBwuHandler( + Mediums& mediums, + IncomingConnectionCallback incoming_connection_callback); private: class BluetoothIncomingSocket : public IncomingSocket { diff --git a/connections/implementation/bwu_handler.h b/connections/implementation/bwu_handler.h index d8ab4750..de1ee7e0 100644 --- a/connections/implementation/bwu_handler.h +++ b/connections/implementation/bwu_handler.h @@ -18,6 +18,7 @@ #include #include +#include "absl/functional/any_invocable.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "connections/implementation/offline_frames.h" @@ -47,12 +48,9 @@ class BwuHandler { std::unique_ptr socket; std::unique_ptr channel; }; - - struct BwuNotifications { - std::function connection)> - incoming_connection_cb; - }; + using IncomingConnectionCallback = absl::AnyInvocable connection)>; virtual ~BwuHandler() = default; diff --git a/connections/implementation/bwu_manager.cc b/connections/implementation/bwu_manager.cc index 1c39963a..97f597d5 100644 --- a/connections/implementation/bwu_manager.cc +++ b/connections/implementation/bwu_manager.cc @@ -103,31 +103,40 @@ BwuManager::~BwuManager() { void BwuManager::InitBwuHandlers() { // Register the supported concrete BwuMedium implementations. - BwuHandler::BwuNotifications notifications{ - .incoming_connection_cb = - absl::bind_front(&BwuManager::OnIncomingConnection, this), - }; if (config_.allow_upgrade_to.wifi_hotspot) { handlers_.emplace( Medium::WIFI_HOTSPOT, - std::make_unique(*mediums_, notifications)); + std::make_unique( + *mediums_, + absl::bind_front(&BwuManager::OnIncomingConnection, this))); } if (config_.allow_upgrade_to.wifi_direct) { handlers_.emplace( Medium::WIFI_DIRECT, - std::make_unique(*mediums_, notifications)); + std::make_unique( + *mediums_, + absl::bind_front(&BwuManager::OnIncomingConnection, this))); } if (config_.allow_upgrade_to.wifi_lan) { - handlers_.emplace(Medium::WIFI_LAN, std::make_unique( - *mediums_, notifications)); + handlers_.emplace( + Medium::WIFI_LAN, + std::make_unique( + *mediums_, + absl::bind_front(&BwuManager::OnIncomingConnection, this))); } if (config_.allow_upgrade_to.web_rtc) { - handlers_.emplace(Medium::WEB_RTC, std::make_unique( - *mediums_, notifications)); + handlers_.emplace( + Medium::WEB_RTC, + std::make_unique( + *mediums_, + absl::bind_front(&BwuManager::OnIncomingConnection, this))); } if (config_.allow_upgrade_to.bluetooth) { - handlers_.emplace(Medium::BLUETOOTH, std::make_unique( - *mediums_, notifications)); + handlers_.emplace( + Medium::BLUETOOTH, + std::make_unique( + *mediums_, + absl::bind_front(&BwuManager::OnIncomingConnection, this))); } } diff --git a/connections/implementation/fake_bwu_handler.h b/connections/implementation/fake_bwu_handler.h index b735dc9e..78c13719 100644 --- a/connections/implementation/fake_bwu_handler.h +++ b/connections/implementation/fake_bwu_handler.h @@ -47,7 +47,7 @@ class FakeBwuHandler : public BaseBwuHandler { }; explicit FakeBwuHandler(Medium medium) - : BaseBwuHandler(BwuNotifications{}), medium_(medium) {} + : BaseBwuHandler(nullptr), medium_(medium) {} ~FakeBwuHandler() override = default; const std::vector& create_calls() const { return create_calls_; } diff --git a/connections/implementation/webrtc_bwu_handler.cc b/connections/implementation/webrtc_bwu_handler.cc index 1941abca..1fbdbb21 100644 --- a/connections/implementation/webrtc_bwu_handler.cc +++ b/connections/implementation/webrtc_bwu_handler.cc @@ -40,9 +40,10 @@ void WebrtcBwuHandler::WebrtcIncomingSocket::Close() { socket_.Close(); } std::string WebrtcBwuHandler::WebrtcIncomingSocket::ToString() { return name_; } -WebrtcBwuHandler::WebrtcBwuHandler(Mediums& mediums, - BwuNotifications notifications) - : BaseBwuHandler(std::move(notifications)), mediums_(mediums) {} +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. @@ -146,7 +147,7 @@ void WebrtcBwuHandler::OnIncomingWebrtcConnection( new IncomingSocketConnection{std::move(webrtc_socket), std::move(channel)}); - bwu_notifications_.incoming_connection_cb(client, std::move(connection)); + NotifyOnIncomingConnection(client, std::move(connection)); } } // namespace connections diff --git a/connections/implementation/webrtc_bwu_handler.h b/connections/implementation/webrtc_bwu_handler.h index 46faae34..c45f7946 100644 --- a/connections/implementation/webrtc_bwu_handler.h +++ b/connections/implementation/webrtc_bwu_handler.h @@ -32,7 +32,9 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class WebrtcBwuHandler : public BaseBwuHandler { public: - explicit WebrtcBwuHandler(Mediums& mediums, BwuNotifications notifications); + explicit WebrtcBwuHandler( + Mediums& mediums, + IncomingConnectionCallback incoming_connection_callback); private: class WebrtcIncomingSocket : public BwuHandler::IncomingSocket { diff --git a/connections/implementation/webrtc_bwu_handler_stub.cc b/connections/implementation/webrtc_bwu_handler_stub.cc index f4590027..91f19c1f 100644 --- a/connections/implementation/webrtc_bwu_handler_stub.cc +++ b/connections/implementation/webrtc_bwu_handler_stub.cc @@ -37,9 +37,9 @@ void WebrtcBwuHandler::WebrtcIncomingSocket::Close() {} std::string WebrtcBwuHandler::WebrtcIncomingSocket::ToString() { return ""; } -WebrtcBwuHandler::WebrtcBwuHandler(Mediums& mediums, - BwuNotifications notifications) - : BaseBwuHandler(std::move(notifications)), +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, diff --git a/connections/implementation/webrtc_bwu_handler_stub.h b/connections/implementation/webrtc_bwu_handler_stub.h index f4aec1ac..de0a9ff1 100644 --- a/connections/implementation/webrtc_bwu_handler_stub.h +++ b/connections/implementation/webrtc_bwu_handler_stub.h @@ -36,7 +36,9 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class WebrtcBwuHandler : public BaseBwuHandler { public: - explicit WebrtcBwuHandler(Mediums& mediums, BwuNotifications notifications); + explicit WebrtcBwuHandler( + Mediums& mediums, + IncomingConnectionCallback incoming_connection_callback); private: class WebrtcIncomingSocket : public BwuHandler::IncomingSocket { diff --git a/connections/implementation/wifi_direct_bwu_handler.cc b/connections/implementation/wifi_direct_bwu_handler.cc index c7ed4317..daa5d04b 100644 --- a/connections/implementation/wifi_direct_bwu_handler.cc +++ b/connections/implementation/wifi_direct_bwu_handler.cc @@ -28,9 +28,10 @@ namespace nearby { namespace connections { -WifiDirectBwuHandler::WifiDirectBwuHandler(Mediums& mediums, - BwuNotifications notifications) - : BaseBwuHandler(std::move(notifications)), mediums_(mediums) {} +WifiDirectBwuHandler::WifiDirectBwuHandler( + Mediums& mediums, IncomingConnectionCallback incoming_connection_callback) + : BaseBwuHandler(std::move(incoming_connection_callback)), + mediums_(mediums) {} ByteArray WifiDirectBwuHandler::HandleInitializeUpgradedMediumForEndpoint( ClientProxy* client, const std::string& upgrade_service_id, @@ -149,7 +150,7 @@ void WifiDirectBwuHandler::OnIncomingWifiDirectConnection( upgrade_service_id, socket), .channel = std::move(channel), }); - bwu_notifications_.incoming_connection_cb(client, std::move(connection)); + NotifyOnIncomingConnection(client, std::move(connection)); } } // namespace connections diff --git a/connections/implementation/wifi_direct_bwu_handler.h b/connections/implementation/wifi_direct_bwu_handler.h index 47424259..e7f159ea 100644 --- a/connections/implementation/wifi_direct_bwu_handler.h +++ b/connections/implementation/wifi_direct_bwu_handler.h @@ -29,14 +29,15 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class WifiDirectBwuHandler : public BaseBwuHandler { public: - explicit WifiDirectBwuHandler(Mediums& mediums, - BwuNotifications notifications); + explicit WifiDirectBwuHandler( + Mediums& mediums, + IncomingConnectionCallback incoming_connection_callback); private: class WifiDirectIncomingSocket : public BwuHandler::IncomingSocket { public: explicit WifiDirectIncomingSocket(const std::string& name, - WifiDirectSocket socket) + WifiDirectSocket socket) : name_(name), socket_(socket) {} std::string ToString() override { return name_; } @@ -73,8 +74,8 @@ class WifiDirectBwuHandler : public BaseBwuHandler { // Accept Connection Callback. void OnIncomingWifiDirectConnection(ClientProxy* client, - const std::string& upgrade_service_id, - WifiDirectSocket socket); + const std::string& upgrade_service_id, + WifiDirectSocket socket); Mediums& mediums_; Wifi& wifi_medium_ = mediums_.GetWifi(); diff --git a/connections/implementation/wifi_direct_bwu_test.cc b/connections/implementation/wifi_direct_bwu_test.cc index 127065f2..b01d9998 100644 --- a/connections/implementation/wifi_direct_bwu_test.cc +++ b/connections/implementation/wifi_direct_bwu_test.cc @@ -38,11 +38,10 @@ class WifiDirectTest : public testing::Test { }; TEST_F(WifiDirectTest, CanCreateBwuHandler) { - BwuHandler::BwuNotifications notifications = {.incoming_connection_cb = {}}; ClientProxy client; Mediums mediums; - auto handler = std::make_unique(mediums, notifications); + auto handler = std::make_unique(mediums, nullptr); handler->InitializeUpgradedMediumForEndpoint(&client, /*service_id=*/"B", /*endpoint_id=*/"2"); @@ -56,29 +55,23 @@ TEST_F(WifiDirectTest, WFDGOBWUInit_GCCreateEndpointChannel) { CountDownLatch accept_latch(1); CountDownLatch end_latch(1); - BwuHandler::BwuNotifications notifications_1{ - .incoming_connection_cb = - [&accept_latch, &end_latch]( - ClientProxy* client, - std::unique_ptr - mutable_connection) { - NEARBY_LOGS(WARNING) << "Server socket connection accept call back"; - std::shared_ptr connection( - mutable_connection.release()); - accept_latch.CountDown(); - EXPECT_TRUE(end_latch.Await(kWaitDuration).result()); - NEARBY_LOGS(WARNING) << "Test is done. Close the socket"; - connection->channel->Close(); - connection->socket->Close(); - }, - }; - BwuHandler::BwuNotifications notifications_2 = {.incoming_connection_cb = {}}; ClientProxy wifi_direct_go, wifi_direct_gc; Mediums mediums_1, mediums_2; ExceptionOr upgrade_frame; - auto handler_1 = - std::make_unique(mediums_1, notifications_1); + auto handler_1 = std::make_unique( + mediums_1, [&](ClientProxy* client, + std::unique_ptr + mutable_connection) { + NEARBY_LOGS(WARNING) << "Server socket connection accept call back"; + std::shared_ptr connection( + mutable_connection.release()); + accept_latch.CountDown(); + EXPECT_TRUE(end_latch.Await(kWaitDuration).result()); + NEARBY_LOGS(WARNING) << "Test is done. Close the socket"; + connection->channel->Close(); + connection->socket->Close(); + }); SingleThreadExecutor server_executor; server_executor.Execute( @@ -98,10 +91,9 @@ TEST_F(WifiDirectTest, WFDGOBWUInit_GCCreateEndpointChannel) { EXPECT_TRUE(start_latch.Await(kWaitDuration).result()); EXPECT_FALSE(mediums_2.GetWifiDirect().IsConnectedToGO()); std::unique_ptr handler_2 = - std::make_unique(mediums_2, notifications_2); + std::make_unique(mediums_2, nullptr); - client_executor.Execute([&handler_2, &wifi_direct_gc, &upgrade_frame, - &accept_latch, &end_latch, &mediums_2]() { + client_executor.Execute([&]() { auto bwu_frame = upgrade_frame.result().v1().bandwidth_upgrade_negotiation(); diff --git a/connections/implementation/wifi_hotspot_bwu_handler.cc b/connections/implementation/wifi_hotspot_bwu_handler.cc index 58acf86d..4bf7f215 100644 --- a/connections/implementation/wifi_hotspot_bwu_handler.cc +++ b/connections/implementation/wifi_hotspot_bwu_handler.cc @@ -29,9 +29,10 @@ namespace nearby { namespace connections { -WifiHotspotBwuHandler::WifiHotspotBwuHandler(Mediums& mediums, - BwuNotifications notifications) - : BaseBwuHandler(std::move(notifications)), mediums_(mediums) {} +WifiHotspotBwuHandler::WifiHotspotBwuHandler( + Mediums& mediums, IncomingConnectionCallback incoming_connection_callback) + : BaseBwuHandler(std::move(incoming_connection_callback)), + mediums_(mediums) {} // Called by BWU initiator. Set up WifiHotspot upgraded medium for this // endpoint, and returns a upgrade path info (SSID, Password, Gateway used as @@ -157,7 +158,7 @@ void WifiHotspotBwuHandler::OnIncomingWifiHotspotConnection( upgrade_service_id, socket), .channel = std::move(channel), }); - bwu_notifications_.incoming_connection_cb(client, std::move(connection)); + NotifyOnIncomingConnection(client, std::move(connection)); } } // namespace connections diff --git a/connections/implementation/wifi_hotspot_bwu_handler.h b/connections/implementation/wifi_hotspot_bwu_handler.h index f7668c45..3e4fd611 100644 --- a/connections/implementation/wifi_hotspot_bwu_handler.h +++ b/connections/implementation/wifi_hotspot_bwu_handler.h @@ -29,14 +29,15 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class WifiHotspotBwuHandler : public BaseBwuHandler { public: - explicit WifiHotspotBwuHandler(Mediums& mediums, - BwuNotifications notifications); + explicit WifiHotspotBwuHandler( + Mediums& mediums, + IncomingConnectionCallback incoming_connection_callback); private: class WifiHotspotIncomingSocket : public BwuHandler::IncomingSocket { public: explicit WifiHotspotIncomingSocket(const std::string& name, - WifiHotspotSocket socket) + WifiHotspotSocket socket) : name_(name), socket_(socket) {} std::string ToString() override { return name_; } @@ -64,8 +65,8 @@ class WifiHotspotBwuHandler : public BaseBwuHandler { const std::string& upgrade_service_id) final; void OnIncomingWifiHotspotConnection(ClientProxy* client, - const std::string& upgrade_service_id, - WifiHotspotSocket socket); + const std::string& upgrade_service_id, + WifiHotspotSocket socket); Mediums& mediums_; WifiHotspot& wifi_hotspot_medium_{mediums_.GetWifiHotspot()}; diff --git a/connections/implementation/wifi_hotspot_test.cc b/connections/implementation/wifi_hotspot_test.cc index c24a2988..091087c3 100644 --- a/connections/implementation/wifi_hotspot_test.cc +++ b/connections/implementation/wifi_hotspot_test.cc @@ -37,12 +37,10 @@ class WifiHotspotTest : public testing::Test { }; TEST_F(WifiHotspotTest, CanCreateBwuHandler) { - BwuHandler::BwuNotifications notifications{.incoming_connection_cb = {}}; ClientProxy client; Mediums mediums; - auto handler = - std::make_unique(mediums, notifications); + auto handler = std::make_unique(mediums, nullptr); handler->InitializeUpgradedMediumForEndpoint(&client, /*service_id=*/"B", /*endpoint_id=*/"2"); @@ -56,48 +54,40 @@ TEST_F(WifiHotspotTest, SoftAPBWUInit_STACreateEndpointChannel) { CountDownLatch accept_latch(1); CountDownLatch end_latch(1); - BwuHandler::BwuNotifications notifications_1{ - .incoming_connection_cb = - [&accept_latch, &end_latch]( - ClientProxy* client, - std::unique_ptr - mutable_connection) { - NEARBY_LOGS(WARNING) << "Server socket connection accept call back"; - accept_latch.CountDown(); - EXPECT_TRUE(end_latch.Await(kWaitDuration).result()); - }, - }; - BwuHandler::BwuNotifications notifications_2{.incoming_connection_cb = {}}; ClientProxy client_1, client_2; Mediums mediums_1, mediums_2; ExceptionOr upgrade_frame; - auto handler_1 = - std::make_unique(mediums_1, notifications_1); + auto handler_1 = std::make_unique( + mediums_1, [&](ClientProxy* client, + std::unique_ptr + mutable_connection) { + NEARBY_LOGS(WARNING) << "Server socket connection accept call back"; + accept_latch.CountDown(); + EXPECT_TRUE(end_latch.Await(kWaitDuration).result()); + }); // client_1 works as Hotspot SoftAP SingleThreadExecutor server_executor; - server_executor.Execute( - [&handler_1, &client_1, &upgrade_frame, &start_latch]() { - ByteArray upgrade_path_available_frame = - handler_1->InitializeUpgradedMediumForEndpoint(&client_1, - /*service_id=*/"A", - /*endpoint_id=*/"1"); - EXPECT_FALSE(upgrade_path_available_frame.Empty()); + server_executor.Execute([&]() { + ByteArray upgrade_path_available_frame = + handler_1->InitializeUpgradedMediumForEndpoint(&client_1, + /*service_id=*/"A", + /*endpoint_id=*/"1"); + EXPECT_FALSE(upgrade_path_available_frame.Empty()); - upgrade_frame = parser::FromBytes(upgrade_path_available_frame); - start_latch.CountDown(); - }); + upgrade_frame = parser::FromBytes(upgrade_path_available_frame); + start_latch.CountDown(); + }); // client_2 works as Hotspot STA which will connect to client_1 SingleThreadExecutor client_executor; // Wait till client_1 started as hotspot and then connect to it EXPECT_TRUE(start_latch.Await(kWaitDuration).result()); std::unique_ptr handler_2 = - std::make_unique(mediums_2, notifications_2); + std::make_unique(mediums_2, nullptr); - client_executor.Execute([&handler_2, &client_2, &upgrade_frame, &accept_latch, - &end_latch, &mediums_2]() { + client_executor.Execute([&]() { auto bwu_frame = upgrade_frame.result().v1().bandwidth_upgrade_negotiation(); diff --git a/connections/implementation/wifi_lan_bwu_handler.cc b/connections/implementation/wifi_lan_bwu_handler.cc index 3c74764d..04a0ea50 100644 --- a/connections/implementation/wifi_lan_bwu_handler.cc +++ b/connections/implementation/wifi_lan_bwu_handler.cc @@ -28,9 +28,10 @@ namespace nearby { namespace connections { -WifiLanBwuHandler::WifiLanBwuHandler(Mediums& mediums, - BwuNotifications notifications) - : BaseBwuHandler(std::move(notifications)), mediums_(mediums) {} +WifiLanBwuHandler::WifiLanBwuHandler( + 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 WifiLan using this info. @@ -153,7 +154,7 @@ void WifiLanBwuHandler::OnIncomingWifiLanConnection( socket), .channel = std::move(channel), }); - bwu_notifications_.incoming_connection_cb(client, std::move(connection)); + NotifyOnIncomingConnection(client, std::move(connection)); } } // namespace connections diff --git a/connections/implementation/wifi_lan_bwu_handler.h b/connections/implementation/wifi_lan_bwu_handler.h index 13d3626b..aee1ae01 100644 --- a/connections/implementation/wifi_lan_bwu_handler.h +++ b/connections/implementation/wifi_lan_bwu_handler.h @@ -29,7 +29,9 @@ namespace connections { // per-Medium-specific operations needed to upgrade an EndpointChannel. class WifiLanBwuHandler : public BaseBwuHandler { public: - explicit WifiLanBwuHandler(Mediums& mediums, BwuNotifications notifications); + explicit WifiLanBwuHandler( + Mediums& mediums, + IncomingConnectionCallback incoming_connection_callback); private: class WifiLanIncomingSocket : public BwuHandler::IncomingSocket { From 021fe80f6f1028336ff95cb085613e1a83080a19 Mon Sep 17 00:00:00 2001 From: Anay Wadhera Date: Sun, 13 Aug 2023 10:04:02 -0400 Subject: [PATCH 076/128] Update compiled_proto --- .../proto/offline_wire_formats.pb.cc | 1014 +++++++++++++++- .../proto/offline_wire_formats.pb.h | 1036 +++++++++++++++- compiled_proto/proto/connections_enums.pb.cc | 1078 +++++++++++++++++ compiled_proto/proto/connections_enums.pb.h | 284 +++++ compiled_proto/proto/mediums/ble_frames.pb.cc | 64 +- compiled_proto/proto/mediums/ble_frames.pb.h | 95 +- compiled_proto/proto/sharing_enums.pb.cc | 955 +++++++++++++-- compiled_proto/proto/sharing_enums.pb.h | 250 +++- 8 files changed, 4550 insertions(+), 226 deletions(-) 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 33628b31..7e041c19 100644 --- a/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.cc +++ b/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.cc @@ -41,6 +41,8 @@ constexpr V1Frame::V1Frame( , paired_key_encryption_(nullptr) , authentication_message_(nullptr) , authentication_result_(nullptr) + , auto_resume_(nullptr) + , auto_reconnect_(nullptr) , type_(0) {} struct V1FrameDefaultTypeInternal { @@ -83,7 +85,8 @@ constexpr ConnectionResponseFrame::ConnectionResponseFrame( , response_(0) , multiplex_socket_bitmask_(0) - , nearby_connections_version_(0){} + , nearby_connections_version_(0) + , safe_to_disconnect_version_(0){} struct ConnectionResponseFrameDefaultTypeInternal { constexpr ConnectionResponseFrameDefaultTypeInternal() : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} @@ -115,7 +118,8 @@ constexpr PayloadTransferFrame_PayloadChunk::PayloadTransferFrame_PayloadChunk( ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) : body_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) , offset_(int64_t{0}) - , flags_(0){} + , flags_(0) + , index_(0){} struct PayloadTransferFrame_PayloadChunkDefaultTypeInternal { constexpr PayloadTransferFrame_PayloadChunkDefaultTypeInternal() : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} @@ -304,7 +308,8 @@ struct BandwidthUpgradeNegotiationFrameDefaultTypeInternal { PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT BandwidthUpgradeNegotiationFrameDefaultTypeInternal _BandwidthUpgradeNegotiationFrame_default_instance_; constexpr KeepAliveFrame::KeepAliveFrame( ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) - : ack_(false){} + : ack_(false) + , seq_num_(0u){} struct KeepAliveFrameDefaultTypeInternal { constexpr KeepAliveFrameDefaultTypeInternal() : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} @@ -363,6 +368,35 @@ struct AuthenticationResultFrameDefaultTypeInternal { }; }; PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT AuthenticationResultFrameDefaultTypeInternal _AuthenticationResultFrame_default_instance_; +constexpr AutoResumeFrame::AutoResumeFrame( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : pending_payload_id_(int64_t{0}) + , event_type_(0) + + , next_payload_chunk_index_(0){} +struct AutoResumeFrameDefaultTypeInternal { + constexpr AutoResumeFrameDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~AutoResumeFrameDefaultTypeInternal() {} + union { + AutoResumeFrame _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT AutoResumeFrameDefaultTypeInternal _AutoResumeFrame_default_instance_; +constexpr AutoReconnectFrame::AutoReconnectFrame( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : endpoint_id_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , event_type_(0) +{} +struct AutoReconnectFrameDefaultTypeInternal { + constexpr AutoReconnectFrameDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~AutoReconnectFrameDefaultTypeInternal() {} + union { + AutoReconnectFrame _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT AutoReconnectFrameDefaultTypeInternal _AutoReconnectFrame_default_instance_; constexpr MediumMetadata::MediumMetadata( ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) : bssid_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) @@ -606,17 +640,21 @@ bool V1Frame_FrameType_IsValid(int value) { case 7: case 8: case 9: + case 10: + case 11: return true; default: return false; } } -static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed V1Frame_FrameType_strings[10] = {}; +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed V1Frame_FrameType_strings[12] = {}; static const char V1Frame_FrameType_names[] = "AUTHENTICATION_MESSAGE" "AUTHENTICATION_RESULT" + "AUTO_RECONNECT" + "AUTO_RESUME" "BANDWIDTH_UPGRADE_NEGOTIATION" "CONNECTION_REQUEST" "CONNECTION_RESPONSE" @@ -629,27 +667,31 @@ static const char V1Frame_FrameType_names[] = static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry V1Frame_FrameType_entries[] = { { {V1Frame_FrameType_names + 0, 22}, 8 }, { {V1Frame_FrameType_names + 22, 21}, 9 }, - { {V1Frame_FrameType_names + 43, 29}, 4 }, - { {V1Frame_FrameType_names + 72, 18}, 1 }, - { {V1Frame_FrameType_names + 90, 19}, 2 }, - { {V1Frame_FrameType_names + 109, 13}, 6 }, - { {V1Frame_FrameType_names + 122, 10}, 5 }, - { {V1Frame_FrameType_names + 132, 21}, 7 }, - { {V1Frame_FrameType_names + 153, 16}, 3 }, - { {V1Frame_FrameType_names + 169, 18}, 0 }, + { {V1Frame_FrameType_names + 43, 14}, 11 }, + { {V1Frame_FrameType_names + 57, 11}, 10 }, + { {V1Frame_FrameType_names + 68, 29}, 4 }, + { {V1Frame_FrameType_names + 97, 18}, 1 }, + { {V1Frame_FrameType_names + 115, 19}, 2 }, + { {V1Frame_FrameType_names + 134, 13}, 6 }, + { {V1Frame_FrameType_names + 147, 10}, 5 }, + { {V1Frame_FrameType_names + 157, 21}, 7 }, + { {V1Frame_FrameType_names + 178, 16}, 3 }, + { {V1Frame_FrameType_names + 194, 18}, 0 }, }; static const int V1Frame_FrameType_entries_by_number[] = { - 9, // 0 -> UNKNOWN_FRAME_TYPE - 3, // 1 -> CONNECTION_REQUEST - 4, // 2 -> CONNECTION_RESPONSE - 8, // 3 -> PAYLOAD_TRANSFER - 2, // 4 -> BANDWIDTH_UPGRADE_NEGOTIATION - 6, // 5 -> KEEP_ALIVE - 5, // 6 -> DISCONNECTION - 7, // 7 -> PAIRED_KEY_ENCRYPTION + 11, // 0 -> UNKNOWN_FRAME_TYPE + 5, // 1 -> CONNECTION_REQUEST + 6, // 2 -> CONNECTION_RESPONSE + 10, // 3 -> PAYLOAD_TRANSFER + 4, // 4 -> BANDWIDTH_UPGRADE_NEGOTIATION + 8, // 5 -> KEEP_ALIVE + 7, // 6 -> DISCONNECTION + 9, // 7 -> PAIRED_KEY_ENCRYPTION 0, // 8 -> AUTHENTICATION_MESSAGE 1, // 9 -> AUTHENTICATION_RESULT + 3, // 10 -> AUTO_RESUME + 2, // 11 -> AUTO_RECONNECT }; const std::string& V1Frame_FrameType_Name( @@ -658,12 +700,12 @@ const std::string& V1Frame_FrameType_Name( ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( V1Frame_FrameType_entries, V1Frame_FrameType_entries_by_number, - 10, V1Frame_FrameType_strings); + 12, V1Frame_FrameType_strings); (void) dummy; int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( V1Frame_FrameType_entries, V1Frame_FrameType_entries_by_number, - 10, value); + 12, value); return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : V1Frame_FrameType_strings[idx].get(); } @@ -671,7 +713,7 @@ bool V1Frame_FrameType_Parse( ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, V1Frame_FrameType* value) { int int_value; bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( - V1Frame_FrameType_entries, 10, name, &int_value); + V1Frame_FrameType_entries, 12, name, &int_value); if (success) { *value = static_cast(int_value); } @@ -688,6 +730,8 @@ constexpr V1Frame_FrameType V1Frame::DISCONNECTION; constexpr V1Frame_FrameType V1Frame::PAIRED_KEY_ENCRYPTION; constexpr V1Frame_FrameType V1Frame::AUTHENTICATION_MESSAGE; constexpr V1Frame_FrameType V1Frame::AUTHENTICATION_RESULT; +constexpr V1Frame_FrameType V1Frame::AUTO_RESUME; +constexpr V1Frame_FrameType V1Frame::AUTO_RECONNECT; constexpr V1Frame_FrameType V1Frame::FrameType_MIN; constexpr V1Frame_FrameType V1Frame::FrameType_MAX; constexpr int V1Frame::FrameType_ARRAYSIZE; @@ -1301,6 +1345,132 @@ constexpr BandwidthUpgradeNegotiationFrame_EventType BandwidthUpgradeNegotiation constexpr BandwidthUpgradeNegotiationFrame_EventType BandwidthUpgradeNegotiationFrame::EventType_MAX; constexpr int BandwidthUpgradeNegotiationFrame::EventType_ARRAYSIZE; #endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)) +bool AutoResumeFrame_EventType_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed AutoResumeFrame_EventType_strings[3] = {}; + +static const char AutoResumeFrame_EventType_names[] = + "PAYLOAD_RESUME_TRANSFER_ACK" + "PAYLOAD_RESUME_TRANSFER_START" + "UNKNOWN_AUTO_RESUME_EVENT_TYPE"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry AutoResumeFrame_EventType_entries[] = { + { {AutoResumeFrame_EventType_names + 0, 27}, 2 }, + { {AutoResumeFrame_EventType_names + 27, 29}, 1 }, + { {AutoResumeFrame_EventType_names + 56, 30}, 0 }, +}; + +static const int AutoResumeFrame_EventType_entries_by_number[] = { + 2, // 0 -> UNKNOWN_AUTO_RESUME_EVENT_TYPE + 1, // 1 -> PAYLOAD_RESUME_TRANSFER_START + 0, // 2 -> PAYLOAD_RESUME_TRANSFER_ACK +}; + +const std::string& AutoResumeFrame_EventType_Name( + AutoResumeFrame_EventType value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + AutoResumeFrame_EventType_entries, + AutoResumeFrame_EventType_entries_by_number, + 3, AutoResumeFrame_EventType_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + AutoResumeFrame_EventType_entries, + AutoResumeFrame_EventType_entries_by_number, + 3, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + AutoResumeFrame_EventType_strings[idx].get(); +} +bool AutoResumeFrame_EventType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, AutoResumeFrame_EventType* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + AutoResumeFrame_EventType_entries, 3, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} +#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)) +constexpr AutoResumeFrame_EventType AutoResumeFrame::UNKNOWN_AUTO_RESUME_EVENT_TYPE; +constexpr AutoResumeFrame_EventType AutoResumeFrame::PAYLOAD_RESUME_TRANSFER_START; +constexpr AutoResumeFrame_EventType AutoResumeFrame::PAYLOAD_RESUME_TRANSFER_ACK; +constexpr AutoResumeFrame_EventType AutoResumeFrame::EventType_MIN; +constexpr AutoResumeFrame_EventType AutoResumeFrame::EventType_MAX; +constexpr int AutoResumeFrame::EventType_ARRAYSIZE; +#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)) +bool AutoReconnectFrame_EventType_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed AutoReconnectFrame_EventType_strings[3] = {}; + +static const char AutoReconnectFrame_EventType_names[] = + "CLIENT_INTRODUCTION" + "CLIENT_INTRODUCTION_ACK" + "UNKNOWN_EVENT_TYPE"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry AutoReconnectFrame_EventType_entries[] = { + { {AutoReconnectFrame_EventType_names + 0, 19}, 1 }, + { {AutoReconnectFrame_EventType_names + 19, 23}, 2 }, + { {AutoReconnectFrame_EventType_names + 42, 18}, 0 }, +}; + +static const int AutoReconnectFrame_EventType_entries_by_number[] = { + 2, // 0 -> UNKNOWN_EVENT_TYPE + 0, // 1 -> CLIENT_INTRODUCTION + 1, // 2 -> CLIENT_INTRODUCTION_ACK +}; + +const std::string& AutoReconnectFrame_EventType_Name( + AutoReconnectFrame_EventType value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + AutoReconnectFrame_EventType_entries, + AutoReconnectFrame_EventType_entries_by_number, + 3, AutoReconnectFrame_EventType_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + AutoReconnectFrame_EventType_entries, + AutoReconnectFrame_EventType_entries_by_number, + 3, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + AutoReconnectFrame_EventType_strings[idx].get(); +} +bool AutoReconnectFrame_EventType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, AutoReconnectFrame_EventType* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + AutoReconnectFrame_EventType_entries, 3, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} +#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)) +constexpr AutoReconnectFrame_EventType AutoReconnectFrame::UNKNOWN_EVENT_TYPE; +constexpr AutoReconnectFrame_EventType AutoReconnectFrame::CLIENT_INTRODUCTION; +constexpr AutoReconnectFrame_EventType AutoReconnectFrame::CLIENT_INTRODUCTION_ACK; +constexpr AutoReconnectFrame_EventType AutoReconnectFrame::EventType_MIN; +constexpr AutoReconnectFrame_EventType AutoReconnectFrame::EventType_MAX; +constexpr int AutoReconnectFrame::EventType_ARRAYSIZE; +#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)) bool LocationStandard_Format_IsValid(int value) { switch (value) { case 0: @@ -1839,7 +2009,7 @@ class V1Frame::_Internal { public: using HasBits = decltype(std::declval()._has_bits_); static void set_has_type(HasBits* has_bits) { - (*has_bits)[0] |= 512u; + (*has_bits)[0] |= 2048u; } static const ::location::nearby::connections::ConnectionRequestFrame& connection_request(const V1Frame* msg); static void set_has_connection_request(HasBits* has_bits) { @@ -1877,6 +2047,14 @@ class V1Frame::_Internal { static void set_has_authentication_result(HasBits* has_bits) { (*has_bits)[0] |= 256u; } + static const ::location::nearby::connections::AutoResumeFrame& auto_resume(const V1Frame* msg); + static void set_has_auto_resume(HasBits* has_bits) { + (*has_bits)[0] |= 512u; + } + static const ::location::nearby::connections::AutoReconnectFrame& auto_reconnect(const V1Frame* msg); + static void set_has_auto_reconnect(HasBits* has_bits) { + (*has_bits)[0] |= 1024u; + } }; const ::location::nearby::connections::ConnectionRequestFrame& @@ -1915,6 +2093,14 @@ const ::location::nearby::connections::AuthenticationResultFrame& V1Frame::_Internal::authentication_result(const V1Frame* msg) { return *msg->authentication_result_; } +const ::location::nearby::connections::AutoResumeFrame& +V1Frame::_Internal::auto_resume(const V1Frame* msg) { + return *msg->auto_resume_; +} +const ::location::nearby::connections::AutoReconnectFrame& +V1Frame::_Internal::auto_reconnect(const V1Frame* msg) { + return *msg->auto_reconnect_; +} V1Frame::V1Frame(::PROTOBUF_NAMESPACE_ID::Arena* arena, bool is_message_owned) : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) { @@ -1973,6 +2159,16 @@ V1Frame::V1Frame(const V1Frame& from) } else { authentication_result_ = nullptr; } + if (from._internal_has_auto_resume()) { + auto_resume_ = new ::location::nearby::connections::AutoResumeFrame(*from.auto_resume_); + } else { + auto_resume_ = nullptr; + } + if (from._internal_has_auto_reconnect()) { + auto_reconnect_ = new ::location::nearby::connections::AutoReconnectFrame(*from.auto_reconnect_); + } else { + auto_reconnect_ = nullptr; + } type_ = from.type_; // @@protoc_insertion_point(copy_constructor:location.nearby.connections.V1Frame) } @@ -2002,6 +2198,8 @@ inline void V1Frame::SharedDtor() { if (this != internal_default_instance()) delete paired_key_encryption_; if (this != internal_default_instance()) delete authentication_message_; if (this != internal_default_instance()) delete authentication_result_; + if (this != internal_default_instance()) delete auto_resume_; + if (this != internal_default_instance()) delete auto_reconnect_; } void V1Frame::ArenaDtor(void* object) { @@ -2055,9 +2253,19 @@ void V1Frame::Clear() { authentication_message_->Clear(); } } - if (cached_has_bits & 0x00000100u) { - GOOGLE_DCHECK(authentication_result_ != nullptr); - authentication_result_->Clear(); + if (cached_has_bits & 0x00000700u) { + if (cached_has_bits & 0x00000100u) { + GOOGLE_DCHECK(authentication_result_ != nullptr); + authentication_result_->Clear(); + } + if (cached_has_bits & 0x00000200u) { + GOOGLE_DCHECK(auto_resume_ != nullptr); + auto_resume_->Clear(); + } + if (cached_has_bits & 0x00000400u) { + GOOGLE_DCHECK(auto_reconnect_ != nullptr); + auto_reconnect_->Clear(); + } } type_ = 0; _has_bits_.Clear(); @@ -2156,6 +2364,22 @@ const char* V1Frame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::in } else goto handle_unusual; continue; + // optional .location.nearby.connections.AutoResumeFrame auto_resume = 11; + case 11: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 90)) { + ptr = ctx->ParseMessage(_internal_mutable_auto_resume(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional .location.nearby.connections.AutoReconnectFrame auto_reconnect = 12; + case 12: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 98)) { + ptr = ctx->ParseMessage(_internal_mutable_auto_reconnect(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; default: goto handle_unusual; } // switch @@ -2188,7 +2412,7 @@ uint8_t* V1Frame::_InternalSerialize( cached_has_bits = _has_bits_[0]; // optional .location.nearby.connections.V1Frame.FrameType type = 1; - if (cached_has_bits & 0x00000200u) { + if (cached_has_bits & 0x00000800u) { target = stream->EnsureSpace(target); target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( 1, this->_internal_type(), target); @@ -2266,6 +2490,22 @@ uint8_t* V1Frame::_InternalSerialize( 10, _Internal::authentication_result(this), target, stream); } + // optional .location.nearby.connections.AutoResumeFrame auto_resume = 11; + if (cached_has_bits & 0x00000200u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 11, _Internal::auto_resume(this), target, stream); + } + + // optional .location.nearby.connections.AutoReconnectFrame auto_reconnect = 12; + if (cached_has_bits & 0x00000400u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 12, _Internal::auto_reconnect(this), target, stream); + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { target = stream->WriteRaw(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(), static_cast(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target); @@ -2341,7 +2581,7 @@ size_t V1Frame::ByteSizeLong() const { } } - if (cached_has_bits & 0x00000300u) { + if (cached_has_bits & 0x00000f00u) { // optional .location.nearby.connections.AuthenticationResultFrame authentication_result = 10; if (cached_has_bits & 0x00000100u) { total_size += 1 + @@ -2349,8 +2589,22 @@ size_t V1Frame::ByteSizeLong() const { *authentication_result_); } - // optional .location.nearby.connections.V1Frame.FrameType type = 1; + // optional .location.nearby.connections.AutoResumeFrame auto_resume = 11; if (cached_has_bits & 0x00000200u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *auto_resume_); + } + + // optional .location.nearby.connections.AutoReconnectFrame auto_reconnect = 12; + if (cached_has_bits & 0x00000400u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *auto_reconnect_); + } + + // optional .location.nearby.connections.V1Frame.FrameType type = 1; + if (cached_has_bits & 0x00000800u) { total_size += 1 + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_type()); } @@ -2403,11 +2657,17 @@ void V1Frame::MergeFrom(const V1Frame& from) { _internal_mutable_authentication_message()->::location::nearby::connections::AuthenticationMessageFrame::MergeFrom(from._internal_authentication_message()); } } - if (cached_has_bits & 0x00000300u) { + if (cached_has_bits & 0x00000f00u) { if (cached_has_bits & 0x00000100u) { _internal_mutable_authentication_result()->::location::nearby::connections::AuthenticationResultFrame::MergeFrom(from._internal_authentication_result()); } if (cached_has_bits & 0x00000200u) { + _internal_mutable_auto_resume()->::location::nearby::connections::AutoResumeFrame::MergeFrom(from._internal_auto_resume()); + } + if (cached_has_bits & 0x00000400u) { + _internal_mutable_auto_reconnect()->::location::nearby::connections::AutoReconnectFrame::MergeFrom(from._internal_auto_reconnect()); + } + if (cached_has_bits & 0x00000800u) { type_ = from.type_; } _has_bits_[0] |= cached_has_bits; @@ -3248,6 +3508,9 @@ class ConnectionResponseFrame::_Internal { static void set_has_nearby_connections_version(HasBits* has_bits) { (*has_bits)[0] |= 32u; } + static void set_has_safe_to_disconnect_version(HasBits* has_bits) { + (*has_bits)[0] |= 64u; + } }; const ::location::nearby::connections::OsInfo& @@ -3281,8 +3544,8 @@ ConnectionResponseFrame::ConnectionResponseFrame(const ConnectionResponseFrame& os_info_ = nullptr; } ::memcpy(&status_, &from.status_, - static_cast(reinterpret_cast(&nearby_connections_version_) - - reinterpret_cast(&status_)) + sizeof(nearby_connections_version_)); + static_cast(reinterpret_cast(&safe_to_disconnect_version_) - + reinterpret_cast(&status_)) + sizeof(safe_to_disconnect_version_)); // @@protoc_insertion_point(copy_constructor:location.nearby.connections.ConnectionResponseFrame) } @@ -3293,8 +3556,8 @@ handshake_data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStr #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ::memset(reinterpret_cast(this) + static_cast( reinterpret_cast(&os_info_) - reinterpret_cast(this)), - 0, static_cast(reinterpret_cast(&nearby_connections_version_) - - reinterpret_cast(&os_info_)) + sizeof(nearby_connections_version_)); + 0, static_cast(reinterpret_cast(&safe_to_disconnect_version_) - + reinterpret_cast(&os_info_)) + sizeof(safe_to_disconnect_version_)); } ConnectionResponseFrame::~ConnectionResponseFrame() { @@ -3336,10 +3599,10 @@ void ConnectionResponseFrame::Clear() { os_info_->Clear(); } } - if (cached_has_bits & 0x0000003cu) { + if (cached_has_bits & 0x0000007cu) { ::memset(&status_, 0, static_cast( - reinterpret_cast(&nearby_connections_version_) - - reinterpret_cast(&status_)) + sizeof(nearby_connections_version_)); + reinterpret_cast(&safe_to_disconnect_version_) - + reinterpret_cast(&status_)) + sizeof(safe_to_disconnect_version_)); } _has_bits_.Clear(); _internal_metadata_.Clear(); @@ -3400,7 +3663,7 @@ const char* ConnectionResponseFrame::_InternalParse(const char* ptr, ::PROTOBUF_ } else goto handle_unusual; continue; - // optional int32 nearby_connections_version = 6; + // optional int32 nearby_connections_version = 6 [deprecated = true]; case 6: if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 48)) { _Internal::set_has_nearby_connections_version(&has_bits); @@ -3409,6 +3672,15 @@ const char* ConnectionResponseFrame::_InternalParse(const char* ptr, ::PROTOBUF_ } else goto handle_unusual; continue; + // optional int32 safe_to_disconnect_version = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 56)) { + _Internal::set_has_safe_to_disconnect_version(&has_bits); + safe_to_disconnect_version_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; default: goto handle_unusual; } // switch @@ -3473,12 +3745,18 @@ uint8_t* ConnectionResponseFrame::_InternalSerialize( target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(5, this->_internal_multiplex_socket_bitmask(), target); } - // optional int32 nearby_connections_version = 6; + // optional int32 nearby_connections_version = 6 [deprecated = true]; if (cached_has_bits & 0x00000020u) { target = stream->EnsureSpace(target); target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(6, this->_internal_nearby_connections_version(), target); } + // optional int32 safe_to_disconnect_version = 7; + if (cached_has_bits & 0x00000040u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(7, this->_internal_safe_to_disconnect_version(), target); + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { target = stream->WriteRaw(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(), static_cast(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target); @@ -3496,7 +3774,7 @@ size_t ConnectionResponseFrame::ByteSizeLong() const { (void) cached_has_bits; cached_has_bits = _has_bits_[0]; - if (cached_has_bits & 0x0000003fu) { + if (cached_has_bits & 0x0000007fu) { // optional bytes handshake_data = 2; if (cached_has_bits & 0x00000001u) { total_size += 1 + @@ -3527,11 +3805,16 @@ size_t ConnectionResponseFrame::ByteSizeLong() const { total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_multiplex_socket_bitmask()); } - // optional int32 nearby_connections_version = 6; + // optional int32 nearby_connections_version = 6 [deprecated = true]; if (cached_has_bits & 0x00000020u) { total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_nearby_connections_version()); } + // optional int32 safe_to_disconnect_version = 7; + if (cached_has_bits & 0x00000040u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_safe_to_disconnect_version()); + } + } if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { total_size += _internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size(); @@ -3554,7 +3837,7 @@ void ConnectionResponseFrame::MergeFrom(const ConnectionResponseFrame& from) { (void) cached_has_bits; cached_has_bits = from._has_bits_[0]; - if (cached_has_bits & 0x0000003fu) { + if (cached_has_bits & 0x0000007fu) { if (cached_has_bits & 0x00000001u) { _internal_set_handshake_data(from._internal_handshake_data()); } @@ -3573,6 +3856,9 @@ void ConnectionResponseFrame::MergeFrom(const ConnectionResponseFrame& from) { if (cached_has_bits & 0x00000020u) { nearby_connections_version_ = from.nearby_connections_version_; } + if (cached_has_bits & 0x00000040u) { + safe_to_disconnect_version_ = from.safe_to_disconnect_version_; + } _has_bits_[0] |= cached_has_bits; } _internal_metadata_.MergeFrom(from._internal_metadata_); @@ -3601,8 +3887,8 @@ void ConnectionResponseFrame::InternalSwap(ConnectionResponseFrame* other) { &other->handshake_data_, rhs_arena ); ::PROTOBUF_NAMESPACE_ID::internal::memswap< - PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, nearby_connections_version_) - + sizeof(ConnectionResponseFrame::nearby_connections_version_) + PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, safe_to_disconnect_version_) + + sizeof(ConnectionResponseFrame::safe_to_disconnect_version_) - PROTOBUF_FIELD_OFFSET(ConnectionResponseFrame, os_info_)>( reinterpret_cast(&os_info_), reinterpret_cast(&other->os_info_)); @@ -4021,6 +4307,9 @@ class PayloadTransferFrame_PayloadChunk::_Internal { static void set_has_body(HasBits* has_bits) { (*has_bits)[0] |= 1u; } + static void set_has_index(HasBits* has_bits) { + (*has_bits)[0] |= 8u; + } }; PayloadTransferFrame_PayloadChunk::PayloadTransferFrame_PayloadChunk(::PROTOBUF_NAMESPACE_ID::Arena* arena, @@ -4045,8 +4334,8 @@ PayloadTransferFrame_PayloadChunk::PayloadTransferFrame_PayloadChunk(const Paylo GetArenaForAllocation()); } ::memcpy(&offset_, &from.offset_, - static_cast(reinterpret_cast(&flags_) - - reinterpret_cast(&offset_)) + sizeof(flags_)); + static_cast(reinterpret_cast(&index_) - + reinterpret_cast(&offset_)) + sizeof(index_)); // @@protoc_insertion_point(copy_constructor:location.nearby.connections.PayloadTransferFrame.PayloadChunk) } @@ -4057,8 +4346,8 @@ body_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlready #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING ::memset(reinterpret_cast(this) + static_cast( reinterpret_cast(&offset_) - reinterpret_cast(this)), - 0, static_cast(reinterpret_cast(&flags_) - - reinterpret_cast(&offset_)) + sizeof(flags_)); + 0, static_cast(reinterpret_cast(&index_) - + reinterpret_cast(&offset_)) + sizeof(index_)); } PayloadTransferFrame_PayloadChunk::~PayloadTransferFrame_PayloadChunk() { @@ -4093,10 +4382,10 @@ void PayloadTransferFrame_PayloadChunk::Clear() { if (cached_has_bits & 0x00000001u) { body_.ClearNonDefaultToEmpty(); } - if (cached_has_bits & 0x00000006u) { + if (cached_has_bits & 0x0000000eu) { ::memset(&offset_, 0, static_cast( - reinterpret_cast(&flags_) - - reinterpret_cast(&offset_)) + sizeof(flags_)); + reinterpret_cast(&index_) - + reinterpret_cast(&offset_)) + sizeof(index_)); } _has_bits_.Clear(); _internal_metadata_.Clear(); @@ -4136,6 +4425,15 @@ const char* PayloadTransferFrame_PayloadChunk::_InternalParse(const char* ptr, : } else goto handle_unusual; continue; + // optional int32 index = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 32)) { + _Internal::set_has_index(&has_bits); + index_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; default: goto handle_unusual; } // switch @@ -4185,6 +4483,12 @@ uint8_t* PayloadTransferFrame_PayloadChunk::_InternalSerialize( 3, this->_internal_body(), target); } + // optional int32 index = 4; + if (cached_has_bits & 0x00000008u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(4, this->_internal_index(), target); + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { target = stream->WriteRaw(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(), static_cast(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target); @@ -4202,7 +4506,7 @@ size_t PayloadTransferFrame_PayloadChunk::ByteSizeLong() const { (void) cached_has_bits; cached_has_bits = _has_bits_[0]; - if (cached_has_bits & 0x00000007u) { + if (cached_has_bits & 0x0000000fu) { // optional bytes body = 3; if (cached_has_bits & 0x00000001u) { total_size += 1 + @@ -4220,6 +4524,11 @@ size_t PayloadTransferFrame_PayloadChunk::ByteSizeLong() const { total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_flags()); } + // optional int32 index = 4; + if (cached_has_bits & 0x00000008u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_index()); + } + } if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { total_size += _internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size(); @@ -4242,7 +4551,7 @@ void PayloadTransferFrame_PayloadChunk::MergeFrom(const PayloadTransferFrame_Pay (void) cached_has_bits; cached_has_bits = from._has_bits_[0]; - if (cached_has_bits & 0x00000007u) { + if (cached_has_bits & 0x0000000fu) { if (cached_has_bits & 0x00000001u) { _internal_set_body(from._internal_body()); } @@ -4252,6 +4561,9 @@ void PayloadTransferFrame_PayloadChunk::MergeFrom(const PayloadTransferFrame_Pay if (cached_has_bits & 0x00000004u) { flags_ = from.flags_; } + if (cached_has_bits & 0x00000008u) { + index_ = from.index_; + } _has_bits_[0] |= cached_has_bits; } _internal_metadata_.MergeFrom(from._internal_metadata_); @@ -4280,8 +4592,8 @@ void PayloadTransferFrame_PayloadChunk::InternalSwap(PayloadTransferFrame_Payloa &other->body_, rhs_arena ); ::PROTOBUF_NAMESPACE_ID::internal::memswap< - PROTOBUF_FIELD_OFFSET(PayloadTransferFrame_PayloadChunk, flags_) - + sizeof(PayloadTransferFrame_PayloadChunk::flags_) + PROTOBUF_FIELD_OFFSET(PayloadTransferFrame_PayloadChunk, index_) + + sizeof(PayloadTransferFrame_PayloadChunk::index_) - PROTOBUF_FIELD_OFFSET(PayloadTransferFrame_PayloadChunk, offset_)>( reinterpret_cast(&offset_), reinterpret_cast(&other->offset_)); @@ -8034,6 +8346,9 @@ class KeepAliveFrame::_Internal { static void set_has_ack(HasBits* has_bits) { (*has_bits)[0] |= 1u; } + static void set_has_seq_num(HasBits* has_bits) { + (*has_bits)[0] |= 2u; + } }; KeepAliveFrame::KeepAliveFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, @@ -8049,12 +8364,17 @@ KeepAliveFrame::KeepAliveFrame(const KeepAliveFrame& from) : ::PROTOBUF_NAMESPACE_ID::MessageLite(), _has_bits_(from._has_bits_) { _internal_metadata_.MergeFrom(from._internal_metadata_); - ack_ = from.ack_; + ::memcpy(&ack_, &from.ack_, + static_cast(reinterpret_cast(&seq_num_) - + reinterpret_cast(&ack_)) + sizeof(seq_num_)); // @@protoc_insertion_point(copy_constructor:location.nearby.connections.KeepAliveFrame) } inline void KeepAliveFrame::SharedCtor() { -ack_ = false; +::memset(reinterpret_cast(this) + static_cast( + reinterpret_cast(&ack_) - reinterpret_cast(this)), + 0, static_cast(reinterpret_cast(&seq_num_) - + reinterpret_cast(&ack_)) + sizeof(seq_num_)); } KeepAliveFrame::~KeepAliveFrame() { @@ -8084,7 +8404,12 @@ void KeepAliveFrame::Clear() { // Prevent compiler warnings about cached_has_bits being unused (void) cached_has_bits; - ack_ = false; + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000003u) { + ::memset(&ack_, 0, static_cast( + reinterpret_cast(&seq_num_) - + reinterpret_cast(&ack_)) + sizeof(seq_num_)); + } _has_bits_.Clear(); _internal_metadata_.Clear(); } @@ -8105,6 +8430,15 @@ const char* KeepAliveFrame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE } else goto handle_unusual; continue; + // optional uint32 seq_num = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 16)) { + _Internal::set_has_seq_num(&has_bits); + seq_num_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; default: goto handle_unusual; } // switch @@ -8142,6 +8476,12 @@ uint8_t* KeepAliveFrame::_InternalSerialize( target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(1, this->_internal_ack(), target); } + // optional uint32 seq_num = 2; + if (cached_has_bits & 0x00000002u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt32ToArray(2, this->_internal_seq_num(), target); + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { target = stream->WriteRaw(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(), static_cast(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target); @@ -8158,12 +8498,19 @@ size_t KeepAliveFrame::ByteSizeLong() const { // Prevent compiler warnings about cached_has_bits being unused (void) cached_has_bits; - // optional bool ack = 1; cached_has_bits = _has_bits_[0]; - if (cached_has_bits & 0x00000001u) { - total_size += 1 + 1; - } + if (cached_has_bits & 0x00000003u) { + // optional bool ack = 1; + if (cached_has_bits & 0x00000001u) { + total_size += 1 + 1; + } + // optional uint32 seq_num = 2; + if (cached_has_bits & 0x00000002u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt32SizePlusOne(this->_internal_seq_num()); + } + + } if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { total_size += _internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size(); } @@ -8184,8 +8531,15 @@ void KeepAliveFrame::MergeFrom(const KeepAliveFrame& from) { uint32_t cached_has_bits = 0; (void) cached_has_bits; - if (from._internal_has_ack()) { - _internal_set_ack(from._internal_ack()); + cached_has_bits = from._has_bits_[0]; + if (cached_has_bits & 0x00000003u) { + if (cached_has_bits & 0x00000001u) { + ack_ = from.ack_; + } + if (cached_has_bits & 0x00000002u) { + seq_num_ = from.seq_num_; + } + _has_bits_[0] |= cached_has_bits; } _internal_metadata_.MergeFrom(from._internal_metadata_); } @@ -8205,7 +8559,12 @@ void KeepAliveFrame::InternalSwap(KeepAliveFrame* other) { using std::swap; _internal_metadata_.InternalSwap(&other->_internal_metadata_); swap(_has_bits_[0], other->_has_bits_[0]); - swap(ack_, other->ack_); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(KeepAliveFrame, seq_num_) + + sizeof(KeepAliveFrame::seq_num_) + - PROTOBUF_FIELD_OFFSET(KeepAliveFrame, ack_)>( + reinterpret_cast(&ack_), + reinterpret_cast(&other->ack_)); } std::string KeepAliveFrame::GetTypeName() const { @@ -9049,6 +9408,523 @@ std::string AuthenticationResultFrame::GetTypeName() const { } +// =================================================================== + +class AutoResumeFrame::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static void set_has_event_type(HasBits* has_bits) { + (*has_bits)[0] |= 2u; + } + static void set_has_pending_payload_id(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } + static void set_has_next_payload_chunk_index(HasBits* has_bits) { + (*has_bits)[0] |= 4u; + } +}; + +AutoResumeFrame::AutoResumeFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:location.nearby.connections.AutoResumeFrame) +} +AutoResumeFrame::AutoResumeFrame(const AutoResumeFrame& from) + : ::PROTOBUF_NAMESPACE_ID::MessageLite(), + _has_bits_(from._has_bits_) { + _internal_metadata_.MergeFrom(from._internal_metadata_); + ::memcpy(&pending_payload_id_, &from.pending_payload_id_, + static_cast(reinterpret_cast(&next_payload_chunk_index_) - + reinterpret_cast(&pending_payload_id_)) + sizeof(next_payload_chunk_index_)); + // @@protoc_insertion_point(copy_constructor:location.nearby.connections.AutoResumeFrame) +} + +inline void AutoResumeFrame::SharedCtor() { +::memset(reinterpret_cast(this) + static_cast( + reinterpret_cast(&pending_payload_id_) - reinterpret_cast(this)), + 0, static_cast(reinterpret_cast(&next_payload_chunk_index_) - + reinterpret_cast(&pending_payload_id_)) + sizeof(next_payload_chunk_index_)); +} + +AutoResumeFrame::~AutoResumeFrame() { + // @@protoc_insertion_point(destructor:location.nearby.connections.AutoResumeFrame) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete(); +} + +inline void AutoResumeFrame::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); +} + +void AutoResumeFrame::ArenaDtor(void* object) { + AutoResumeFrame* _this = reinterpret_cast< AutoResumeFrame* >(object); + (void)_this; +} +void AutoResumeFrame::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void AutoResumeFrame::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void AutoResumeFrame::Clear() { +// @@protoc_insertion_point(message_clear_start:location.nearby.connections.AutoResumeFrame) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000007u) { + ::memset(&pending_payload_id_, 0, static_cast( + reinterpret_cast(&next_payload_chunk_index_) - + reinterpret_cast(&pending_payload_id_)) + sizeof(next_payload_chunk_index_)); + } + _has_bits_.Clear(); + _internal_metadata_.Clear(); +} + +const char* AutoResumeFrame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // optional .location.nearby.connections.AutoResumeFrame.EventType event_type = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + if (PROTOBUF_PREDICT_TRUE(::location::nearby::connections::AutoResumeFrame_EventType_IsValid(val))) { + _internal_set_event_type(static_cast<::location::nearby::connections::AutoResumeFrame_EventType>(val)); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields()); + } + } else + goto handle_unusual; + continue; + // optional int64 pending_payload_id = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 16)) { + _Internal::set_has_pending_payload_id(&has_bits); + pending_payload_id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 next_payload_chunk_index = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 24)) { + _Internal::set_has_next_payload_chunk_index(&has_bits); + next_payload_chunk_index_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* AutoResumeFrame::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:location.nearby.connections.AutoResumeFrame) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + // optional .location.nearby.connections.AutoResumeFrame.EventType event_type = 1; + if (cached_has_bits & 0x00000002u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 1, this->_internal_event_type(), target); + } + + // optional int64 pending_payload_id = 2; + if (cached_has_bits & 0x00000001u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(2, this->_internal_pending_payload_id(), target); + } + + // optional int32 next_payload_chunk_index = 3; + if (cached_has_bits & 0x00000004u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(3, this->_internal_next_payload_chunk_index(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = stream->WriteRaw(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(), + static_cast(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target); + } + // @@protoc_insertion_point(serialize_to_array_end:location.nearby.connections.AutoResumeFrame) + return target; +} + +size_t AutoResumeFrame::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:location.nearby.connections.AutoResumeFrame) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000007u) { + // optional int64 pending_payload_id = 2; + if (cached_has_bits & 0x00000001u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64SizePlusOne(this->_internal_pending_payload_id()); + } + + // optional .location.nearby.connections.AutoResumeFrame.EventType event_type = 1; + if (cached_has_bits & 0x00000002u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_event_type()); + } + + // optional int32 next_payload_chunk_index = 3; + if (cached_has_bits & 0x00000004u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_next_payload_chunk_index()); + } + + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + total_size += _internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size(); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void AutoResumeFrame::CheckTypeAndMergeFrom( + const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) { + MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast( + &from)); +} + +void AutoResumeFrame::MergeFrom(const AutoResumeFrame& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:location.nearby.connections.AutoResumeFrame) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = from._has_bits_[0]; + if (cached_has_bits & 0x00000007u) { + if (cached_has_bits & 0x00000001u) { + pending_payload_id_ = from.pending_payload_id_; + } + if (cached_has_bits & 0x00000002u) { + event_type_ = from.event_type_; + } + if (cached_has_bits & 0x00000004u) { + next_payload_chunk_index_ = from.next_payload_chunk_index_; + } + _has_bits_[0] |= cached_has_bits; + } + _internal_metadata_.MergeFrom(from._internal_metadata_); +} + +void AutoResumeFrame::CopyFrom(const AutoResumeFrame& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:location.nearby.connections.AutoResumeFrame) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool AutoResumeFrame::IsInitialized() const { + return true; +} + +void AutoResumeFrame::InternalSwap(AutoResumeFrame* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(AutoResumeFrame, next_payload_chunk_index_) + + sizeof(AutoResumeFrame::next_payload_chunk_index_) + - PROTOBUF_FIELD_OFFSET(AutoResumeFrame, pending_payload_id_)>( + reinterpret_cast(&pending_payload_id_), + reinterpret_cast(&other->pending_payload_id_)); +} + +std::string AutoResumeFrame::GetTypeName() const { + return "location.nearby.connections.AutoResumeFrame"; +} + + +// =================================================================== + +class AutoReconnectFrame::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static void set_has_endpoint_id(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } + static void set_has_event_type(HasBits* has_bits) { + (*has_bits)[0] |= 2u; + } +}; + +AutoReconnectFrame::AutoReconnectFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::MessageLite(arena, is_message_owned) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:location.nearby.connections.AutoReconnectFrame) +} +AutoReconnectFrame::AutoReconnectFrame(const AutoReconnectFrame& from) + : ::PROTOBUF_NAMESPACE_ID::MessageLite(), + _has_bits_(from._has_bits_) { + _internal_metadata_.MergeFrom(from._internal_metadata_); + endpoint_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + endpoint_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_endpoint_id()) { + endpoint_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_endpoint_id(), + GetArenaForAllocation()); + } + event_type_ = from.event_type_; + // @@protoc_insertion_point(copy_constructor:location.nearby.connections.AutoReconnectFrame) +} + +inline void AutoReconnectFrame::SharedCtor() { +endpoint_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + endpoint_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +event_type_ = 0; +} + +AutoReconnectFrame::~AutoReconnectFrame() { + // @@protoc_insertion_point(destructor:location.nearby.connections.AutoReconnectFrame) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete(); +} + +inline void AutoReconnectFrame::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + endpoint_id_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void AutoReconnectFrame::ArenaDtor(void* object) { + AutoReconnectFrame* _this = reinterpret_cast< AutoReconnectFrame* >(object); + (void)_this; +} +void AutoReconnectFrame::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void AutoReconnectFrame::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void AutoReconnectFrame::Clear() { +// @@protoc_insertion_point(message_clear_start:location.nearby.connections.AutoReconnectFrame) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000001u) { + endpoint_id_.ClearNonDefaultToEmpty(); + } + event_type_ = 0; + _has_bits_.Clear(); + _internal_metadata_.Clear(); +} + +const char* AutoReconnectFrame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // optional string endpoint_id = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_endpoint_id(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional .location.nearby.connections.AutoReconnectFrame.EventType event_type = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 16)) { + uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + if (PROTOBUF_PREDICT_TRUE(::location::nearby::connections::AutoReconnectFrame_EventType_IsValid(val))) { + _internal_set_event_type(static_cast<::location::nearby::connections::AutoReconnectFrame_EventType>(val)); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(2, val, mutable_unknown_fields()); + } + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* AutoReconnectFrame::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:location.nearby.connections.AutoReconnectFrame) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + // optional string endpoint_id = 1; + if (cached_has_bits & 0x00000001u) { + target = stream->WriteStringMaybeAliased( + 1, this->_internal_endpoint_id(), target); + } + + // optional .location.nearby.connections.AutoReconnectFrame.EventType event_type = 2; + if (cached_has_bits & 0x00000002u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 2, this->_internal_event_type(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = stream->WriteRaw(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(), + static_cast(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target); + } + // @@protoc_insertion_point(serialize_to_array_end:location.nearby.connections.AutoReconnectFrame) + return target; +} + +size_t AutoReconnectFrame::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:location.nearby.connections.AutoReconnectFrame) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000003u) { + // optional string endpoint_id = 1; + if (cached_has_bits & 0x00000001u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_endpoint_id()); + } + + // optional .location.nearby.connections.AutoReconnectFrame.EventType event_type = 2; + if (cached_has_bits & 0x00000002u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_event_type()); + } + + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + total_size += _internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size(); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void AutoReconnectFrame::CheckTypeAndMergeFrom( + const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) { + MergeFrom(*::PROTOBUF_NAMESPACE_ID::internal::DownCast( + &from)); +} + +void AutoReconnectFrame::MergeFrom(const AutoReconnectFrame& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:location.nearby.connections.AutoReconnectFrame) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = from._has_bits_[0]; + if (cached_has_bits & 0x00000003u) { + if (cached_has_bits & 0x00000001u) { + _internal_set_endpoint_id(from._internal_endpoint_id()); + } + if (cached_has_bits & 0x00000002u) { + event_type_ = from.event_type_; + } + _has_bits_[0] |= cached_has_bits; + } + _internal_metadata_.MergeFrom(from._internal_metadata_); +} + +void AutoReconnectFrame::CopyFrom(const AutoReconnectFrame& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:location.nearby.connections.AutoReconnectFrame) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool AutoReconnectFrame::IsInitialized() const { + return true; +} + +void AutoReconnectFrame::InternalSwap(AutoReconnectFrame* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &endpoint_id_, lhs_arena, + &other->endpoint_id_, rhs_arena + ); + swap(event_type_, other->event_type_); +} + +std::string AutoReconnectFrame::GetTypeName() const { + return "location.nearby.connections.AutoReconnectFrame"; +} + + // =================================================================== class MediumMetadata::_Internal { @@ -12222,6 +13098,12 @@ template<> PROTOBUF_NOINLINE ::location::nearby::connections::AuthenticationMess template<> PROTOBUF_NOINLINE ::location::nearby::connections::AuthenticationResultFrame* Arena::CreateMaybeMessage< ::location::nearby::connections::AuthenticationResultFrame >(Arena* arena) { return Arena::CreateMessageInternal< ::location::nearby::connections::AuthenticationResultFrame >(arena); } +template<> PROTOBUF_NOINLINE ::location::nearby::connections::AutoResumeFrame* Arena::CreateMaybeMessage< ::location::nearby::connections::AutoResumeFrame >(Arena* arena) { + return Arena::CreateMessageInternal< ::location::nearby::connections::AutoResumeFrame >(arena); +} +template<> PROTOBUF_NOINLINE ::location::nearby::connections::AutoReconnectFrame* Arena::CreateMaybeMessage< ::location::nearby::connections::AutoReconnectFrame >(Arena* arena) { + return Arena::CreateMessageInternal< ::location::nearby::connections::AutoReconnectFrame >(arena); +} template<> PROTOBUF_NOINLINE ::location::nearby::connections::MediumMetadata* Arena::CreateMaybeMessage< ::location::nearby::connections::MediumMetadata >(Arena* arena) { return Arena::CreateMessageInternal< ::location::nearby::connections::MediumMetadata >(arena); } 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 c75669f6..22ca5e23 100644 --- a/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.h +++ b/compiled_proto/connections/implementation/proto/offline_wire_formats.pb.h @@ -45,7 +45,7 @@ struct TableStruct_connections_2fimplementation_2fproto_2foffline_5fwire_5fforma PROTOBUF_SECTION_VARIABLE(protodesc_cold); static const ::PROTOBUF_NAMESPACE_ID::internal::AuxiliaryParseTableField aux[] PROTOBUF_SECTION_VARIABLE(protodesc_cold); - static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[34] + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[36] PROTOBUF_SECTION_VARIABLE(protodesc_cold); static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; @@ -60,6 +60,12 @@ extern AuthenticationMessageFrameDefaultTypeInternal _AuthenticationMessageFrame class AuthenticationResultFrame; struct AuthenticationResultFrameDefaultTypeInternal; extern AuthenticationResultFrameDefaultTypeInternal _AuthenticationResultFrame_default_instance_; +class AutoReconnectFrame; +struct AutoReconnectFrameDefaultTypeInternal; +extern AutoReconnectFrameDefaultTypeInternal _AutoReconnectFrame_default_instance_; +class AutoResumeFrame; +struct AutoResumeFrameDefaultTypeInternal; +extern AutoResumeFrameDefaultTypeInternal _AutoResumeFrame_default_instance_; class AvailableChannels; struct AvailableChannelsDefaultTypeInternal; extern AvailableChannelsDefaultTypeInternal _AvailableChannels_default_instance_; @@ -162,6 +168,8 @@ extern WifiLanUsableChannelsDefaultTypeInternal _WifiLanUsableChannels_default_i PROTOBUF_NAMESPACE_OPEN template<> ::location::nearby::connections::AuthenticationMessageFrame* Arena::CreateMaybeMessage<::location::nearby::connections::AuthenticationMessageFrame>(Arena*); template<> ::location::nearby::connections::AuthenticationResultFrame* Arena::CreateMaybeMessage<::location::nearby::connections::AuthenticationResultFrame>(Arena*); +template<> ::location::nearby::connections::AutoReconnectFrame* Arena::CreateMaybeMessage<::location::nearby::connections::AutoReconnectFrame>(Arena*); +template<> ::location::nearby::connections::AutoResumeFrame* Arena::CreateMaybeMessage<::location::nearby::connections::AutoResumeFrame>(Arena*); template<> ::location::nearby::connections::AvailableChannels* Arena::CreateMaybeMessage<::location::nearby::connections::AvailableChannels>(Arena*); template<> ::location::nearby::connections::BandwidthUpgradeNegotiationFrame* Arena::CreateMaybeMessage<::location::nearby::connections::BandwidthUpgradeNegotiationFrame>(Arena*); template<> ::location::nearby::connections::BandwidthUpgradeNegotiationFrame_ClientIntroduction* Arena::CreateMaybeMessage<::location::nearby::connections::BandwidthUpgradeNegotiationFrame_ClientIntroduction>(Arena*); @@ -228,11 +236,13 @@ enum V1Frame_FrameType : int { V1Frame_FrameType_DISCONNECTION = 6, V1Frame_FrameType_PAIRED_KEY_ENCRYPTION = 7, V1Frame_FrameType_AUTHENTICATION_MESSAGE = 8, - V1Frame_FrameType_AUTHENTICATION_RESULT = 9 + V1Frame_FrameType_AUTHENTICATION_RESULT = 9, + V1Frame_FrameType_AUTO_RESUME = 10, + V1Frame_FrameType_AUTO_RECONNECT = 11 }; bool V1Frame_FrameType_IsValid(int value); constexpr V1Frame_FrameType V1Frame_FrameType_FrameType_MIN = V1Frame_FrameType_UNKNOWN_FRAME_TYPE; -constexpr V1Frame_FrameType V1Frame_FrameType_FrameType_MAX = V1Frame_FrameType_AUTHENTICATION_RESULT; +constexpr V1Frame_FrameType V1Frame_FrameType_FrameType_MAX = V1Frame_FrameType_AUTO_RECONNECT; constexpr int V1Frame_FrameType_FrameType_ARRAYSIZE = V1Frame_FrameType_FrameType_MAX + 1; const std::string& V1Frame_FrameType_Name(V1Frame_FrameType value); @@ -426,6 +436,46 @@ inline const std::string& BandwidthUpgradeNegotiationFrame_EventType_Name(T enum } bool BandwidthUpgradeNegotiationFrame_EventType_Parse( ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, BandwidthUpgradeNegotiationFrame_EventType* value); +enum AutoResumeFrame_EventType : int { + AutoResumeFrame_EventType_UNKNOWN_AUTO_RESUME_EVENT_TYPE = 0, + AutoResumeFrame_EventType_PAYLOAD_RESUME_TRANSFER_START = 1, + AutoResumeFrame_EventType_PAYLOAD_RESUME_TRANSFER_ACK = 2 +}; +bool AutoResumeFrame_EventType_IsValid(int value); +constexpr AutoResumeFrame_EventType AutoResumeFrame_EventType_EventType_MIN = AutoResumeFrame_EventType_UNKNOWN_AUTO_RESUME_EVENT_TYPE; +constexpr AutoResumeFrame_EventType AutoResumeFrame_EventType_EventType_MAX = AutoResumeFrame_EventType_PAYLOAD_RESUME_TRANSFER_ACK; +constexpr int AutoResumeFrame_EventType_EventType_ARRAYSIZE = AutoResumeFrame_EventType_EventType_MAX + 1; + +const std::string& AutoResumeFrame_EventType_Name(AutoResumeFrame_EventType value); +template +inline const std::string& AutoResumeFrame_EventType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function AutoResumeFrame_EventType_Name."); + return AutoResumeFrame_EventType_Name(static_cast(enum_t_value)); +} +bool AutoResumeFrame_EventType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, AutoResumeFrame_EventType* value); +enum AutoReconnectFrame_EventType : int { + AutoReconnectFrame_EventType_UNKNOWN_EVENT_TYPE = 0, + AutoReconnectFrame_EventType_CLIENT_INTRODUCTION = 1, + AutoReconnectFrame_EventType_CLIENT_INTRODUCTION_ACK = 2 +}; +bool AutoReconnectFrame_EventType_IsValid(int value); +constexpr AutoReconnectFrame_EventType AutoReconnectFrame_EventType_EventType_MIN = AutoReconnectFrame_EventType_UNKNOWN_EVENT_TYPE; +constexpr AutoReconnectFrame_EventType AutoReconnectFrame_EventType_EventType_MAX = AutoReconnectFrame_EventType_CLIENT_INTRODUCTION_ACK; +constexpr int AutoReconnectFrame_EventType_EventType_ARRAYSIZE = AutoReconnectFrame_EventType_EventType_MAX + 1; + +const std::string& AutoReconnectFrame_EventType_Name(AutoReconnectFrame_EventType value); +template +inline const std::string& AutoReconnectFrame_EventType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function AutoReconnectFrame_EventType_Name."); + return AutoReconnectFrame_EventType_Name(static_cast(enum_t_value)); +} +bool AutoReconnectFrame_EventType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, AutoReconnectFrame_EventType* value); enum LocationStandard_Format : int { LocationStandard_Format_UNKNOWN = 0, LocationStandard_Format_E164_CALLING = 1, @@ -834,6 +884,10 @@ class V1Frame final : V1Frame_FrameType_AUTHENTICATION_MESSAGE; static constexpr FrameType AUTHENTICATION_RESULT = V1Frame_FrameType_AUTHENTICATION_RESULT; + static constexpr FrameType AUTO_RESUME = + V1Frame_FrameType_AUTO_RESUME; + static constexpr FrameType AUTO_RECONNECT = + V1Frame_FrameType_AUTO_RECONNECT; static inline bool FrameType_IsValid(int value) { return V1Frame_FrameType_IsValid(value); } @@ -867,6 +921,8 @@ class V1Frame final : kPairedKeyEncryptionFieldNumber = 8, kAuthenticationMessageFieldNumber = 9, kAuthenticationResultFieldNumber = 10, + kAutoResumeFieldNumber = 11, + kAutoReconnectFieldNumber = 12, kTypeFieldNumber = 1, }; // optional .location.nearby.connections.ConnectionRequestFrame connection_request = 2; @@ -1031,6 +1087,42 @@ class V1Frame final : ::location::nearby::connections::AuthenticationResultFrame* authentication_result); ::location::nearby::connections::AuthenticationResultFrame* unsafe_arena_release_authentication_result(); + // optional .location.nearby.connections.AutoResumeFrame auto_resume = 11; + bool has_auto_resume() const; + private: + bool _internal_has_auto_resume() const; + public: + void clear_auto_resume(); + const ::location::nearby::connections::AutoResumeFrame& auto_resume() const; + PROTOBUF_NODISCARD ::location::nearby::connections::AutoResumeFrame* release_auto_resume(); + ::location::nearby::connections::AutoResumeFrame* mutable_auto_resume(); + void set_allocated_auto_resume(::location::nearby::connections::AutoResumeFrame* auto_resume); + private: + const ::location::nearby::connections::AutoResumeFrame& _internal_auto_resume() const; + ::location::nearby::connections::AutoResumeFrame* _internal_mutable_auto_resume(); + public: + void unsafe_arena_set_allocated_auto_resume( + ::location::nearby::connections::AutoResumeFrame* auto_resume); + ::location::nearby::connections::AutoResumeFrame* unsafe_arena_release_auto_resume(); + + // optional .location.nearby.connections.AutoReconnectFrame auto_reconnect = 12; + bool has_auto_reconnect() const; + private: + bool _internal_has_auto_reconnect() const; + public: + void clear_auto_reconnect(); + const ::location::nearby::connections::AutoReconnectFrame& auto_reconnect() const; + PROTOBUF_NODISCARD ::location::nearby::connections::AutoReconnectFrame* release_auto_reconnect(); + ::location::nearby::connections::AutoReconnectFrame* mutable_auto_reconnect(); + void set_allocated_auto_reconnect(::location::nearby::connections::AutoReconnectFrame* auto_reconnect); + private: + const ::location::nearby::connections::AutoReconnectFrame& _internal_auto_reconnect() const; + ::location::nearby::connections::AutoReconnectFrame* _internal_mutable_auto_reconnect(); + public: + void unsafe_arena_set_allocated_auto_reconnect( + ::location::nearby::connections::AutoReconnectFrame* auto_reconnect); + ::location::nearby::connections::AutoReconnectFrame* unsafe_arena_release_auto_reconnect(); + // optional .location.nearby.connections.V1Frame.FrameType type = 1; bool has_type() const; private: @@ -1062,6 +1154,8 @@ class V1Frame final : ::location::nearby::connections::PairedKeyEncryptionFrame* paired_key_encryption_; ::location::nearby::connections::AuthenticationMessageFrame* authentication_message_; ::location::nearby::connections::AuthenticationResultFrame* authentication_result_; + ::location::nearby::connections::AutoResumeFrame* auto_resume_; + ::location::nearby::connections::AutoReconnectFrame* auto_reconnect_; int type_; friend struct ::TableStruct_connections_2fimplementation_2fproto_2foffline_5fwire_5fformats_2eproto; }; @@ -1645,6 +1739,7 @@ class ConnectionResponseFrame final : kResponseFieldNumber = 3, kMultiplexSocketBitmaskFieldNumber = 5, kNearbyConnectionsVersionFieldNumber = 6, + kSafeToDisconnectVersionFieldNumber = 7, }; // optional bytes handshake_data = 2; bool has_handshake_data() const; @@ -1721,19 +1816,32 @@ class ConnectionResponseFrame final : void _internal_set_multiplex_socket_bitmask(int32_t value); public: - // optional int32 nearby_connections_version = 6; - bool has_nearby_connections_version() const; + // optional int32 nearby_connections_version = 6 [deprecated = true]; + PROTOBUF_DEPRECATED bool has_nearby_connections_version() const; private: bool _internal_has_nearby_connections_version() const; public: - void clear_nearby_connections_version(); - int32_t nearby_connections_version() const; - void set_nearby_connections_version(int32_t value); + PROTOBUF_DEPRECATED void clear_nearby_connections_version(); + PROTOBUF_DEPRECATED int32_t nearby_connections_version() const; + PROTOBUF_DEPRECATED void set_nearby_connections_version(int32_t value); private: int32_t _internal_nearby_connections_version() const; void _internal_set_nearby_connections_version(int32_t value); public: + // optional int32 safe_to_disconnect_version = 7; + bool has_safe_to_disconnect_version() const; + private: + bool _internal_has_safe_to_disconnect_version() const; + public: + void clear_safe_to_disconnect_version(); + int32_t safe_to_disconnect_version() const; + void set_safe_to_disconnect_version(int32_t value); + private: + int32_t _internal_safe_to_disconnect_version() const; + void _internal_set_safe_to_disconnect_version(int32_t value); + public: + // @@protoc_insertion_point(class_scope:location.nearby.connections.ConnectionResponseFrame) private: class _Internal; @@ -1749,6 +1857,7 @@ class ConnectionResponseFrame final : int response_; int32_t multiplex_socket_bitmask_; int32_t nearby_connections_version_; + int32_t safe_to_disconnect_version_; friend struct ::TableStruct_connections_2fimplementation_2fproto_2foffline_5fwire_5fformats_2eproto; }; // ------------------------------------------------------------------- @@ -2150,6 +2259,7 @@ class PayloadTransferFrame_PayloadChunk final : kBodyFieldNumber = 3, kOffsetFieldNumber = 2, kFlagsFieldNumber = 1, + kIndexFieldNumber = 4, }; // optional bytes body = 3; bool has_body() const; @@ -2195,6 +2305,19 @@ class PayloadTransferFrame_PayloadChunk final : void _internal_set_flags(int32_t value); public: + // optional int32 index = 4; + bool has_index() const; + private: + bool _internal_has_index() const; + public: + void clear_index(); + int32_t index() const; + void set_index(int32_t value); + private: + int32_t _internal_index() const; + void _internal_set_index(int32_t value); + public: + // @@protoc_insertion_point(class_scope:location.nearby.connections.PayloadTransferFrame.PayloadChunk) private: class _Internal; @@ -2207,6 +2330,7 @@ class PayloadTransferFrame_PayloadChunk final : ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr body_; int64_t offset_; int32_t flags_; + int32_t index_; friend struct ::TableStruct_connections_2fimplementation_2fproto_2foffline_5fwire_5fformats_2eproto; }; // ------------------------------------------------------------------- @@ -4756,6 +4880,7 @@ class KeepAliveFrame final : enum : int { kAckFieldNumber = 1, + kSeqNumFieldNumber = 2, }; // optional bool ack = 1; bool has_ack() const; @@ -4770,6 +4895,19 @@ class KeepAliveFrame final : void _internal_set_ack(bool value); public: + // optional uint32 seq_num = 2; + bool has_seq_num() const; + private: + bool _internal_has_seq_num() const; + public: + void clear_seq_num(); + uint32_t seq_num() const; + void set_seq_num(uint32_t value); + private: + uint32_t _internal_seq_num() const; + void _internal_set_seq_num(uint32_t value); + public: + // @@protoc_insertion_point(class_scope:location.nearby.connections.KeepAliveFrame) private: class _Internal; @@ -4780,6 +4918,7 @@ class KeepAliveFrame final : ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; bool ack_; + uint32_t seq_num_; friend struct ::TableStruct_connections_2fimplementation_2fproto_2foffline_5fwire_5fformats_2eproto; }; // ------------------------------------------------------------------- @@ -5377,6 +5516,396 @@ class AuthenticationResultFrame final : }; // ------------------------------------------------------------------- +class AutoResumeFrame final : + public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:location.nearby.connections.AutoResumeFrame) */ { + public: + inline AutoResumeFrame() : AutoResumeFrame(nullptr) {} + ~AutoResumeFrame() override; + explicit constexpr AutoResumeFrame(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + AutoResumeFrame(const AutoResumeFrame& from); + AutoResumeFrame(AutoResumeFrame&& from) noexcept + : AutoResumeFrame() { + *this = ::std::move(from); + } + + inline AutoResumeFrame& operator=(const AutoResumeFrame& from) { + CopyFrom(from); + return *this; + } + inline AutoResumeFrame& operator=(AutoResumeFrame&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const std::string& unknown_fields() const { + return _internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString); + } + inline std::string* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields(); + } + + static const AutoResumeFrame& default_instance() { + return *internal_default_instance(); + } + static inline const AutoResumeFrame* internal_default_instance() { + return reinterpret_cast( + &_AutoResumeFrame_default_instance_); + } + static constexpr int kIndexInFileMessages = + 23; + + friend void swap(AutoResumeFrame& a, AutoResumeFrame& b) { + a.Swap(&b); + } + inline void Swap(AutoResumeFrame* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(AutoResumeFrame* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + AutoResumeFrame* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final; + void CopyFrom(const AutoResumeFrame& from); + void MergeFrom(const AutoResumeFrame& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(AutoResumeFrame* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "location.nearby.connections.AutoResumeFrame"; + } + protected: + explicit AutoResumeFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + std::string GetTypeName() const final; + + // nested types ---------------------------------------------------- + + typedef AutoResumeFrame_EventType EventType; + static constexpr EventType UNKNOWN_AUTO_RESUME_EVENT_TYPE = + AutoResumeFrame_EventType_UNKNOWN_AUTO_RESUME_EVENT_TYPE; + static constexpr EventType PAYLOAD_RESUME_TRANSFER_START = + AutoResumeFrame_EventType_PAYLOAD_RESUME_TRANSFER_START; + static constexpr EventType PAYLOAD_RESUME_TRANSFER_ACK = + AutoResumeFrame_EventType_PAYLOAD_RESUME_TRANSFER_ACK; + static inline bool EventType_IsValid(int value) { + return AutoResumeFrame_EventType_IsValid(value); + } + static constexpr EventType EventType_MIN = + AutoResumeFrame_EventType_EventType_MIN; + static constexpr EventType EventType_MAX = + AutoResumeFrame_EventType_EventType_MAX; + static constexpr int EventType_ARRAYSIZE = + AutoResumeFrame_EventType_EventType_ARRAYSIZE; + template + static inline const std::string& EventType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function EventType_Name."); + return AutoResumeFrame_EventType_Name(enum_t_value); + } + static inline bool EventType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name, + EventType* value) { + return AutoResumeFrame_EventType_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + enum : int { + kPendingPayloadIdFieldNumber = 2, + kEventTypeFieldNumber = 1, + kNextPayloadChunkIndexFieldNumber = 3, + }; + // optional int64 pending_payload_id = 2; + bool has_pending_payload_id() const; + private: + bool _internal_has_pending_payload_id() const; + public: + void clear_pending_payload_id(); + int64_t pending_payload_id() const; + void set_pending_payload_id(int64_t value); + private: + int64_t _internal_pending_payload_id() const; + void _internal_set_pending_payload_id(int64_t value); + public: + + // optional .location.nearby.connections.AutoResumeFrame.EventType event_type = 1; + bool has_event_type() const; + private: + bool _internal_has_event_type() const; + public: + void clear_event_type(); + ::location::nearby::connections::AutoResumeFrame_EventType event_type() const; + void set_event_type(::location::nearby::connections::AutoResumeFrame_EventType value); + private: + ::location::nearby::connections::AutoResumeFrame_EventType _internal_event_type() const; + void _internal_set_event_type(::location::nearby::connections::AutoResumeFrame_EventType value); + public: + + // optional int32 next_payload_chunk_index = 3; + bool has_next_payload_chunk_index() const; + private: + bool _internal_has_next_payload_chunk_index() const; + public: + void clear_next_payload_chunk_index(); + int32_t next_payload_chunk_index() const; + void set_next_payload_chunk_index(int32_t value); + private: + int32_t _internal_next_payload_chunk_index() const; + void _internal_set_next_payload_chunk_index(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:location.nearby.connections.AutoResumeFrame) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + int64_t pending_payload_id_; + int event_type_; + int32_t next_payload_chunk_index_; + friend struct ::TableStruct_connections_2fimplementation_2fproto_2foffline_5fwire_5fformats_2eproto; +}; +// ------------------------------------------------------------------- + +class AutoReconnectFrame final : + public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:location.nearby.connections.AutoReconnectFrame) */ { + public: + inline AutoReconnectFrame() : AutoReconnectFrame(nullptr) {} + ~AutoReconnectFrame() override; + explicit constexpr AutoReconnectFrame(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + AutoReconnectFrame(const AutoReconnectFrame& from); + AutoReconnectFrame(AutoReconnectFrame&& from) noexcept + : AutoReconnectFrame() { + *this = ::std::move(from); + } + + inline AutoReconnectFrame& operator=(const AutoReconnectFrame& from) { + CopyFrom(from); + return *this; + } + inline AutoReconnectFrame& operator=(AutoReconnectFrame&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const std::string& unknown_fields() const { + return _internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString); + } + inline std::string* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields(); + } + + static const AutoReconnectFrame& default_instance() { + return *internal_default_instance(); + } + static inline const AutoReconnectFrame* internal_default_instance() { + return reinterpret_cast( + &_AutoReconnectFrame_default_instance_); + } + static constexpr int kIndexInFileMessages = + 24; + + friend void swap(AutoReconnectFrame& a, AutoReconnectFrame& b) { + a.Swap(&b); + } + inline void Swap(AutoReconnectFrame* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(AutoReconnectFrame* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + AutoReconnectFrame* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + void CheckTypeAndMergeFrom(const ::PROTOBUF_NAMESPACE_ID::MessageLite& from) final; + void CopyFrom(const AutoReconnectFrame& from); + void MergeFrom(const AutoReconnectFrame& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(AutoReconnectFrame* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "location.nearby.connections.AutoReconnectFrame"; + } + protected: + explicit AutoReconnectFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + std::string GetTypeName() const final; + + // nested types ---------------------------------------------------- + + typedef AutoReconnectFrame_EventType EventType; + static constexpr EventType UNKNOWN_EVENT_TYPE = + AutoReconnectFrame_EventType_UNKNOWN_EVENT_TYPE; + static constexpr EventType CLIENT_INTRODUCTION = + AutoReconnectFrame_EventType_CLIENT_INTRODUCTION; + static constexpr EventType CLIENT_INTRODUCTION_ACK = + AutoReconnectFrame_EventType_CLIENT_INTRODUCTION_ACK; + static inline bool EventType_IsValid(int value) { + return AutoReconnectFrame_EventType_IsValid(value); + } + static constexpr EventType EventType_MIN = + AutoReconnectFrame_EventType_EventType_MIN; + static constexpr EventType EventType_MAX = + AutoReconnectFrame_EventType_EventType_MAX; + static constexpr int EventType_ARRAYSIZE = + AutoReconnectFrame_EventType_EventType_ARRAYSIZE; + template + static inline const std::string& EventType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function EventType_Name."); + return AutoReconnectFrame_EventType_Name(enum_t_value); + } + static inline bool EventType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name, + EventType* value) { + return AutoReconnectFrame_EventType_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + enum : int { + kEndpointIdFieldNumber = 1, + kEventTypeFieldNumber = 2, + }; + // optional string endpoint_id = 1; + bool has_endpoint_id() const; + private: + bool _internal_has_endpoint_id() const; + public: + void clear_endpoint_id(); + const std::string& endpoint_id() const; + template + void set_endpoint_id(ArgT0&& arg0, ArgT... args); + std::string* mutable_endpoint_id(); + PROTOBUF_NODISCARD std::string* release_endpoint_id(); + void set_allocated_endpoint_id(std::string* endpoint_id); + private: + const std::string& _internal_endpoint_id() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_endpoint_id(const std::string& value); + std::string* _internal_mutable_endpoint_id(); + public: + + // optional .location.nearby.connections.AutoReconnectFrame.EventType event_type = 2; + bool has_event_type() const; + private: + bool _internal_has_event_type() const; + public: + void clear_event_type(); + ::location::nearby::connections::AutoReconnectFrame_EventType event_type() const; + void set_event_type(::location::nearby::connections::AutoReconnectFrame_EventType value); + private: + ::location::nearby::connections::AutoReconnectFrame_EventType _internal_event_type() const; + void _internal_set_event_type(::location::nearby::connections::AutoReconnectFrame_EventType value); + public: + + // @@protoc_insertion_point(class_scope:location.nearby.connections.AutoReconnectFrame) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr endpoint_id_; + int event_type_; + friend struct ::TableStruct_connections_2fimplementation_2fproto_2foffline_5fwire_5fformats_2eproto; +}; +// ------------------------------------------------------------------- + class MediumMetadata final : public ::PROTOBUF_NAMESPACE_ID::MessageLite /* @@protoc_insertion_point(class_definition:location.nearby.connections.MediumMetadata) */ { public: @@ -5423,7 +5952,7 @@ class MediumMetadata final : &_MediumMetadata_default_instance_); } static constexpr int kIndexInFileMessages = - 23; + 25; friend void swap(MediumMetadata& a, MediumMetadata& b) { a.Swap(&b); @@ -5750,7 +6279,7 @@ class AvailableChannels final : &_AvailableChannels_default_instance_); } static constexpr int kIndexInFileMessages = - 24; + 26; friend void swap(AvailableChannels& a, AvailableChannels& b) { a.Swap(&b); @@ -5901,7 +6430,7 @@ class WifiDirectCliUsableChannels final : &_WifiDirectCliUsableChannels_default_instance_); } static constexpr int kIndexInFileMessages = - 25; + 27; friend void swap(WifiDirectCliUsableChannels& a, WifiDirectCliUsableChannels& b) { a.Swap(&b); @@ -6052,7 +6581,7 @@ class WifiLanUsableChannels final : &_WifiLanUsableChannels_default_instance_); } static constexpr int kIndexInFileMessages = - 26; + 28; friend void swap(WifiLanUsableChannels& a, WifiLanUsableChannels& b) { a.Swap(&b); @@ -6203,7 +6732,7 @@ class WifiAwareUsableChannels final : &_WifiAwareUsableChannels_default_instance_); } static constexpr int kIndexInFileMessages = - 27; + 29; friend void swap(WifiAwareUsableChannels& a, WifiAwareUsableChannels& b) { a.Swap(&b); @@ -6354,7 +6883,7 @@ class WifiHotspotStaUsableChannels final : &_WifiHotspotStaUsableChannels_default_instance_); } static constexpr int kIndexInFileMessages = - 28; + 30; friend void swap(WifiHotspotStaUsableChannels& a, WifiHotspotStaUsableChannels& b) { a.Swap(&b); @@ -6505,7 +7034,7 @@ class LocationHint final : &_LocationHint_default_instance_); } static constexpr int kIndexInFileMessages = - 29; + 31; friend void swap(LocationHint& a, LocationHint& b) { a.Swap(&b); @@ -6667,7 +7196,7 @@ class LocationStandard final : &_LocationStandard_default_instance_); } static constexpr int kIndexInFileMessages = - 30; + 32; friend void swap(LocationStandard& a, LocationStandard& b) { a.Swap(&b); @@ -6819,7 +7348,7 @@ class OsInfo final : &_OsInfo_default_instance_); } static constexpr int kIndexInFileMessages = - 31; + 33; friend void swap(OsInfo& a, OsInfo& b) { a.Swap(&b); @@ -6995,7 +7524,7 @@ class ConnectionsDevice final : &_ConnectionsDevice_default_instance_); } static constexpr int kIndexInFileMessages = - 32; + 34; friend void swap(ConnectionsDevice& a, ConnectionsDevice& b) { a.Swap(&b); @@ -7197,7 +7726,7 @@ class PresenceDevice final : &_PresenceDevice_default_instance_); } static constexpr int kIndexInFileMessages = - 33; + 35; friend void swap(PresenceDevice& a, PresenceDevice& b) { a.Swap(&b); @@ -7643,7 +8172,7 @@ inline void OfflineFrame::set_allocated_v1(::location::nearby::connections::V1Fr // optional .location.nearby.connections.V1Frame.FrameType type = 1; inline bool V1Frame::_internal_has_type() const { - bool value = (_has_bits_[0] & 0x00000200u) != 0; + bool value = (_has_bits_[0] & 0x00000800u) != 0; return value; } inline bool V1Frame::has_type() const { @@ -7651,7 +8180,7 @@ inline bool V1Frame::has_type() const { } inline void V1Frame::clear_type() { type_ = 0; - _has_bits_[0] &= ~0x00000200u; + _has_bits_[0] &= ~0x00000800u; } inline ::location::nearby::connections::V1Frame_FrameType V1Frame::_internal_type() const { return static_cast< ::location::nearby::connections::V1Frame_FrameType >(type_); @@ -7662,7 +8191,7 @@ inline ::location::nearby::connections::V1Frame_FrameType V1Frame::type() const } inline void V1Frame::_internal_set_type(::location::nearby::connections::V1Frame_FrameType value) { assert(::location::nearby::connections::V1Frame_FrameType_IsValid(value)); - _has_bits_[0] |= 0x00000200u; + _has_bits_[0] |= 0x00000800u; type_ = value; } inline void V1Frame::set_type(::location::nearby::connections::V1Frame_FrameType value) { @@ -8480,6 +9009,186 @@ inline void V1Frame::set_allocated_authentication_result(::location::nearby::con // @@protoc_insertion_point(field_set_allocated:location.nearby.connections.V1Frame.authentication_result) } +// optional .location.nearby.connections.AutoResumeFrame auto_resume = 11; +inline bool V1Frame::_internal_has_auto_resume() const { + bool value = (_has_bits_[0] & 0x00000200u) != 0; + PROTOBUF_ASSUME(!value || auto_resume_ != nullptr); + return value; +} +inline bool V1Frame::has_auto_resume() const { + return _internal_has_auto_resume(); +} +inline void V1Frame::clear_auto_resume() { + if (auto_resume_ != nullptr) auto_resume_->Clear(); + _has_bits_[0] &= ~0x00000200u; +} +inline const ::location::nearby::connections::AutoResumeFrame& V1Frame::_internal_auto_resume() const { + const ::location::nearby::connections::AutoResumeFrame* p = auto_resume_; + return p != nullptr ? *p : reinterpret_cast( + ::location::nearby::connections::_AutoResumeFrame_default_instance_); +} +inline const ::location::nearby::connections::AutoResumeFrame& V1Frame::auto_resume() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.V1Frame.auto_resume) + return _internal_auto_resume(); +} +inline void V1Frame::unsafe_arena_set_allocated_auto_resume( + ::location::nearby::connections::AutoResumeFrame* auto_resume) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(auto_resume_); + } + auto_resume_ = auto_resume; + if (auto_resume) { + _has_bits_[0] |= 0x00000200u; + } else { + _has_bits_[0] &= ~0x00000200u; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:location.nearby.connections.V1Frame.auto_resume) +} +inline ::location::nearby::connections::AutoResumeFrame* V1Frame::release_auto_resume() { + _has_bits_[0] &= ~0x00000200u; + ::location::nearby::connections::AutoResumeFrame* temp = auto_resume_; + auto_resume_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::location::nearby::connections::AutoResumeFrame* V1Frame::unsafe_arena_release_auto_resume() { + // @@protoc_insertion_point(field_release:location.nearby.connections.V1Frame.auto_resume) + _has_bits_[0] &= ~0x00000200u; + ::location::nearby::connections::AutoResumeFrame* temp = auto_resume_; + auto_resume_ = nullptr; + return temp; +} +inline ::location::nearby::connections::AutoResumeFrame* V1Frame::_internal_mutable_auto_resume() { + _has_bits_[0] |= 0x00000200u; + if (auto_resume_ == nullptr) { + auto* p = CreateMaybeMessage<::location::nearby::connections::AutoResumeFrame>(GetArenaForAllocation()); + auto_resume_ = p; + } + return auto_resume_; +} +inline ::location::nearby::connections::AutoResumeFrame* V1Frame::mutable_auto_resume() { + ::location::nearby::connections::AutoResumeFrame* _msg = _internal_mutable_auto_resume(); + // @@protoc_insertion_point(field_mutable:location.nearby.connections.V1Frame.auto_resume) + return _msg; +} +inline void V1Frame::set_allocated_auto_resume(::location::nearby::connections::AutoResumeFrame* auto_resume) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete auto_resume_; + } + if (auto_resume) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper<::location::nearby::connections::AutoResumeFrame>::GetOwningArena(auto_resume); + if (message_arena != submessage_arena) { + auto_resume = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, auto_resume, submessage_arena); + } + _has_bits_[0] |= 0x00000200u; + } else { + _has_bits_[0] &= ~0x00000200u; + } + auto_resume_ = auto_resume; + // @@protoc_insertion_point(field_set_allocated:location.nearby.connections.V1Frame.auto_resume) +} + +// optional .location.nearby.connections.AutoReconnectFrame auto_reconnect = 12; +inline bool V1Frame::_internal_has_auto_reconnect() const { + bool value = (_has_bits_[0] & 0x00000400u) != 0; + PROTOBUF_ASSUME(!value || auto_reconnect_ != nullptr); + return value; +} +inline bool V1Frame::has_auto_reconnect() const { + return _internal_has_auto_reconnect(); +} +inline void V1Frame::clear_auto_reconnect() { + if (auto_reconnect_ != nullptr) auto_reconnect_->Clear(); + _has_bits_[0] &= ~0x00000400u; +} +inline const ::location::nearby::connections::AutoReconnectFrame& V1Frame::_internal_auto_reconnect() const { + const ::location::nearby::connections::AutoReconnectFrame* p = auto_reconnect_; + return p != nullptr ? *p : reinterpret_cast( + ::location::nearby::connections::_AutoReconnectFrame_default_instance_); +} +inline const ::location::nearby::connections::AutoReconnectFrame& V1Frame::auto_reconnect() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.V1Frame.auto_reconnect) + return _internal_auto_reconnect(); +} +inline void V1Frame::unsafe_arena_set_allocated_auto_reconnect( + ::location::nearby::connections::AutoReconnectFrame* auto_reconnect) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(auto_reconnect_); + } + auto_reconnect_ = auto_reconnect; + if (auto_reconnect) { + _has_bits_[0] |= 0x00000400u; + } else { + _has_bits_[0] &= ~0x00000400u; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:location.nearby.connections.V1Frame.auto_reconnect) +} +inline ::location::nearby::connections::AutoReconnectFrame* V1Frame::release_auto_reconnect() { + _has_bits_[0] &= ~0x00000400u; + ::location::nearby::connections::AutoReconnectFrame* temp = auto_reconnect_; + auto_reconnect_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::location::nearby::connections::AutoReconnectFrame* V1Frame::unsafe_arena_release_auto_reconnect() { + // @@protoc_insertion_point(field_release:location.nearby.connections.V1Frame.auto_reconnect) + _has_bits_[0] &= ~0x00000400u; + ::location::nearby::connections::AutoReconnectFrame* temp = auto_reconnect_; + auto_reconnect_ = nullptr; + return temp; +} +inline ::location::nearby::connections::AutoReconnectFrame* V1Frame::_internal_mutable_auto_reconnect() { + _has_bits_[0] |= 0x00000400u; + if (auto_reconnect_ == nullptr) { + auto* p = CreateMaybeMessage<::location::nearby::connections::AutoReconnectFrame>(GetArenaForAllocation()); + auto_reconnect_ = p; + } + return auto_reconnect_; +} +inline ::location::nearby::connections::AutoReconnectFrame* V1Frame::mutable_auto_reconnect() { + ::location::nearby::connections::AutoReconnectFrame* _msg = _internal_mutable_auto_reconnect(); + // @@protoc_insertion_point(field_mutable:location.nearby.connections.V1Frame.auto_reconnect) + return _msg; +} +inline void V1Frame::set_allocated_auto_reconnect(::location::nearby::connections::AutoReconnectFrame* auto_reconnect) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete auto_reconnect_; + } + if (auto_reconnect) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper<::location::nearby::connections::AutoReconnectFrame>::GetOwningArena(auto_reconnect); + if (message_arena != submessage_arena) { + auto_reconnect = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, auto_reconnect, submessage_arena); + } + _has_bits_[0] |= 0x00000400u; + } else { + _has_bits_[0] &= ~0x00000400u; + } + auto_reconnect_ = auto_reconnect; + // @@protoc_insertion_point(field_set_allocated:location.nearby.connections.V1Frame.auto_reconnect) +} + // ------------------------------------------------------------------- // ConnectionRequestFrame @@ -9481,7 +10190,7 @@ inline void ConnectionResponseFrame::set_multiplex_socket_bitmask(int32_t value) // @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.multiplex_socket_bitmask) } -// optional int32 nearby_connections_version = 6; +// optional int32 nearby_connections_version = 6 [deprecated = true]; inline bool ConnectionResponseFrame::_internal_has_nearby_connections_version() const { bool value = (_has_bits_[0] & 0x00000020u) != 0; return value; @@ -9509,6 +10218,34 @@ inline void ConnectionResponseFrame::set_nearby_connections_version(int32_t valu // @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.nearby_connections_version) } +// optional int32 safe_to_disconnect_version = 7; +inline bool ConnectionResponseFrame::_internal_has_safe_to_disconnect_version() const { + bool value = (_has_bits_[0] & 0x00000040u) != 0; + return value; +} +inline bool ConnectionResponseFrame::has_safe_to_disconnect_version() const { + return _internal_has_safe_to_disconnect_version(); +} +inline void ConnectionResponseFrame::clear_safe_to_disconnect_version() { + safe_to_disconnect_version_ = 0; + _has_bits_[0] &= ~0x00000040u; +} +inline int32_t ConnectionResponseFrame::_internal_safe_to_disconnect_version() const { + return safe_to_disconnect_version_; +} +inline int32_t ConnectionResponseFrame::safe_to_disconnect_version() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.ConnectionResponseFrame.safe_to_disconnect_version) + return _internal_safe_to_disconnect_version(); +} +inline void ConnectionResponseFrame::_internal_set_safe_to_disconnect_version(int32_t value) { + _has_bits_[0] |= 0x00000040u; + safe_to_disconnect_version_ = value; +} +inline void ConnectionResponseFrame::set_safe_to_disconnect_version(int32_t value) { + _internal_set_safe_to_disconnect_version(value); + // @@protoc_insertion_point(field_set:location.nearby.connections.ConnectionResponseFrame.safe_to_disconnect_version) +} + // ------------------------------------------------------------------- // PayloadTransferFrame_PayloadHeader @@ -9893,6 +10630,34 @@ inline void PayloadTransferFrame_PayloadChunk::set_allocated_body(std::string* b // @@protoc_insertion_point(field_set_allocated:location.nearby.connections.PayloadTransferFrame.PayloadChunk.body) } +// optional int32 index = 4; +inline bool PayloadTransferFrame_PayloadChunk::_internal_has_index() const { + bool value = (_has_bits_[0] & 0x00000008u) != 0; + return value; +} +inline bool PayloadTransferFrame_PayloadChunk::has_index() const { + return _internal_has_index(); +} +inline void PayloadTransferFrame_PayloadChunk::clear_index() { + index_ = 0; + _has_bits_[0] &= ~0x00000008u; +} +inline int32_t PayloadTransferFrame_PayloadChunk::_internal_index() const { + return index_; +} +inline int32_t PayloadTransferFrame_PayloadChunk::index() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.PayloadTransferFrame.PayloadChunk.index) + return _internal_index(); +} +inline void PayloadTransferFrame_PayloadChunk::_internal_set_index(int32_t value) { + _has_bits_[0] |= 0x00000008u; + index_ = value; +} +inline void PayloadTransferFrame_PayloadChunk::set_index(int32_t value) { + _internal_set_index(value); + // @@protoc_insertion_point(field_set:location.nearby.connections.PayloadTransferFrame.PayloadChunk.index) +} + // ------------------------------------------------------------------- // PayloadTransferFrame_ControlMessage @@ -12528,6 +13293,34 @@ inline void KeepAliveFrame::set_ack(bool value) { // @@protoc_insertion_point(field_set:location.nearby.connections.KeepAliveFrame.ack) } +// optional uint32 seq_num = 2; +inline bool KeepAliveFrame::_internal_has_seq_num() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool KeepAliveFrame::has_seq_num() const { + return _internal_has_seq_num(); +} +inline void KeepAliveFrame::clear_seq_num() { + seq_num_ = 0u; + _has_bits_[0] &= ~0x00000002u; +} +inline uint32_t KeepAliveFrame::_internal_seq_num() const { + return seq_num_; +} +inline uint32_t KeepAliveFrame::seq_num() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.KeepAliveFrame.seq_num) + return _internal_seq_num(); +} +inline void KeepAliveFrame::_internal_set_seq_num(uint32_t value) { + _has_bits_[0] |= 0x00000002u; + seq_num_ = value; +} +inline void KeepAliveFrame::set_seq_num(uint32_t value) { + _internal_set_seq_num(value); + // @@protoc_insertion_point(field_set:location.nearby.connections.KeepAliveFrame.seq_num) +} + // ------------------------------------------------------------------- // DisconnectionFrame @@ -12768,6 +13561,197 @@ inline void AuthenticationResultFrame::set_result(int32_t value) { // ------------------------------------------------------------------- +// AutoResumeFrame + +// optional .location.nearby.connections.AutoResumeFrame.EventType event_type = 1; +inline bool AutoResumeFrame::_internal_has_event_type() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool AutoResumeFrame::has_event_type() const { + return _internal_has_event_type(); +} +inline void AutoResumeFrame::clear_event_type() { + event_type_ = 0; + _has_bits_[0] &= ~0x00000002u; +} +inline ::location::nearby::connections::AutoResumeFrame_EventType AutoResumeFrame::_internal_event_type() const { + return static_cast< ::location::nearby::connections::AutoResumeFrame_EventType >(event_type_); +} +inline ::location::nearby::connections::AutoResumeFrame_EventType AutoResumeFrame::event_type() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.AutoResumeFrame.event_type) + return _internal_event_type(); +} +inline void AutoResumeFrame::_internal_set_event_type(::location::nearby::connections::AutoResumeFrame_EventType value) { + assert(::location::nearby::connections::AutoResumeFrame_EventType_IsValid(value)); + _has_bits_[0] |= 0x00000002u; + event_type_ = value; +} +inline void AutoResumeFrame::set_event_type(::location::nearby::connections::AutoResumeFrame_EventType value) { + _internal_set_event_type(value); + // @@protoc_insertion_point(field_set:location.nearby.connections.AutoResumeFrame.event_type) +} + +// optional int64 pending_payload_id = 2; +inline bool AutoResumeFrame::_internal_has_pending_payload_id() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + return value; +} +inline bool AutoResumeFrame::has_pending_payload_id() const { + return _internal_has_pending_payload_id(); +} +inline void AutoResumeFrame::clear_pending_payload_id() { + pending_payload_id_ = int64_t{0}; + _has_bits_[0] &= ~0x00000001u; +} +inline int64_t AutoResumeFrame::_internal_pending_payload_id() const { + return pending_payload_id_; +} +inline int64_t AutoResumeFrame::pending_payload_id() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.AutoResumeFrame.pending_payload_id) + return _internal_pending_payload_id(); +} +inline void AutoResumeFrame::_internal_set_pending_payload_id(int64_t value) { + _has_bits_[0] |= 0x00000001u; + pending_payload_id_ = value; +} +inline void AutoResumeFrame::set_pending_payload_id(int64_t value) { + _internal_set_pending_payload_id(value); + // @@protoc_insertion_point(field_set:location.nearby.connections.AutoResumeFrame.pending_payload_id) +} + +// optional int32 next_payload_chunk_index = 3; +inline bool AutoResumeFrame::_internal_has_next_payload_chunk_index() const { + bool value = (_has_bits_[0] & 0x00000004u) != 0; + return value; +} +inline bool AutoResumeFrame::has_next_payload_chunk_index() const { + return _internal_has_next_payload_chunk_index(); +} +inline void AutoResumeFrame::clear_next_payload_chunk_index() { + next_payload_chunk_index_ = 0; + _has_bits_[0] &= ~0x00000004u; +} +inline int32_t AutoResumeFrame::_internal_next_payload_chunk_index() const { + return next_payload_chunk_index_; +} +inline int32_t AutoResumeFrame::next_payload_chunk_index() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.AutoResumeFrame.next_payload_chunk_index) + return _internal_next_payload_chunk_index(); +} +inline void AutoResumeFrame::_internal_set_next_payload_chunk_index(int32_t value) { + _has_bits_[0] |= 0x00000004u; + next_payload_chunk_index_ = value; +} +inline void AutoResumeFrame::set_next_payload_chunk_index(int32_t value) { + _internal_set_next_payload_chunk_index(value); + // @@protoc_insertion_point(field_set:location.nearby.connections.AutoResumeFrame.next_payload_chunk_index) +} + +// ------------------------------------------------------------------- + +// AutoReconnectFrame + +// optional string endpoint_id = 1; +inline bool AutoReconnectFrame::_internal_has_endpoint_id() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + return value; +} +inline bool AutoReconnectFrame::has_endpoint_id() const { + return _internal_has_endpoint_id(); +} +inline void AutoReconnectFrame::clear_endpoint_id() { + endpoint_id_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000001u; +} +inline const std::string& AutoReconnectFrame::endpoint_id() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.AutoReconnectFrame.endpoint_id) + return _internal_endpoint_id(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void AutoReconnectFrame::set_endpoint_id(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000001u; + endpoint_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:location.nearby.connections.AutoReconnectFrame.endpoint_id) +} +inline std::string* AutoReconnectFrame::mutable_endpoint_id() { + std::string* _s = _internal_mutable_endpoint_id(); + // @@protoc_insertion_point(field_mutable:location.nearby.connections.AutoReconnectFrame.endpoint_id) + return _s; +} +inline const std::string& AutoReconnectFrame::_internal_endpoint_id() const { + return endpoint_id_.Get(); +} +inline void AutoReconnectFrame::_internal_set_endpoint_id(const std::string& value) { + _has_bits_[0] |= 0x00000001u; + endpoint_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* AutoReconnectFrame::_internal_mutable_endpoint_id() { + _has_bits_[0] |= 0x00000001u; + return endpoint_id_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* AutoReconnectFrame::release_endpoint_id() { + // @@protoc_insertion_point(field_release:location.nearby.connections.AutoReconnectFrame.endpoint_id) + if (!_internal_has_endpoint_id()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000001u; + auto* p = endpoint_id_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (endpoint_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + endpoint_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void AutoReconnectFrame::set_allocated_endpoint_id(std::string* endpoint_id) { + if (endpoint_id != nullptr) { + _has_bits_[0] |= 0x00000001u; + } else { + _has_bits_[0] &= ~0x00000001u; + } + endpoint_id_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), endpoint_id, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (endpoint_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + endpoint_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:location.nearby.connections.AutoReconnectFrame.endpoint_id) +} + +// optional .location.nearby.connections.AutoReconnectFrame.EventType event_type = 2; +inline bool AutoReconnectFrame::_internal_has_event_type() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool AutoReconnectFrame::has_event_type() const { + return _internal_has_event_type(); +} +inline void AutoReconnectFrame::clear_event_type() { + event_type_ = 0; + _has_bits_[0] &= ~0x00000002u; +} +inline ::location::nearby::connections::AutoReconnectFrame_EventType AutoReconnectFrame::_internal_event_type() const { + return static_cast< ::location::nearby::connections::AutoReconnectFrame_EventType >(event_type_); +} +inline ::location::nearby::connections::AutoReconnectFrame_EventType AutoReconnectFrame::event_type() const { + // @@protoc_insertion_point(field_get:location.nearby.connections.AutoReconnectFrame.event_type) + return _internal_event_type(); +} +inline void AutoReconnectFrame::_internal_set_event_type(::location::nearby::connections::AutoReconnectFrame_EventType value) { + assert(::location::nearby::connections::AutoReconnectFrame_EventType_IsValid(value)); + _has_bits_[0] |= 0x00000002u; + event_type_ = value; +} +inline void AutoReconnectFrame::set_event_type(::location::nearby::connections::AutoReconnectFrame_EventType value) { + _internal_set_event_type(value); + // @@protoc_insertion_point(field_set:location.nearby.connections.AutoReconnectFrame.event_type) +} + +// ------------------------------------------------------------------- + // MediumMetadata // optional bool supports_5_ghz = 1; @@ -14678,6 +15662,10 @@ PresenceDevice::mutable_identity_type() { // ------------------------------------------------------------------- +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + // @@protoc_insertion_point(namespace_scope) @@ -14697,6 +15685,8 @@ template <> struct is_proto_enum< ::location::nearby::connections::PayloadTransf template <> struct is_proto_enum< ::location::nearby::connections::PayloadTransferFrame_PacketType> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::connections::BandwidthUpgradeNegotiationFrame_UpgradePathInfo_Medium> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::connections::BandwidthUpgradeNegotiationFrame_EventType> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::connections::AutoResumeFrame_EventType> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::connections::AutoReconnectFrame_EventType> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::connections::LocationStandard_Format> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::connections::OsInfo_OsType> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::connections::PresenceDevice_DeviceType> : ::std::true_type {}; diff --git a/compiled_proto/proto/connections_enums.pb.cc b/compiled_proto/proto/connections_enums.pb.cc index e5fac4cc..20cd2d6f 100644 --- a/compiled_proto/proto/connections_enums.pb.cc +++ b/compiled_proto/proto/connections_enums.pb.cc @@ -1414,6 +1414,1084 @@ bool PowerLevel_Parse( } return success; } +bool OperationResultCategory_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed OperationResultCategory_strings[10] = {}; + +static const char OperationResultCategory_names[] = + "CATEGORY_CLIENT_CANCELLATION" + "CATEGORY_CLIENT_ERROR" + "CATEGORY_CONNECTIVITY_ERROR" + "CATEGORY_DEVICE_STATE_ERROR" + "CATEGORY_IO_ERROR" + "CATEGORY_MEDIUM_UNAVAILABLE" + "CATEGORY_MISCELLANEOUS" + "CATEGORY_NEARBY_ERROR" + "CATEGORY_SUCCESS" + "CATEGORY_UNKNOWN"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry OperationResultCategory_entries[] = { + { {OperationResultCategory_names + 0, 28}, 2 }, + { {OperationResultCategory_names + 28, 21}, 5 }, + { {OperationResultCategory_names + 49, 27}, 7 }, + { {OperationResultCategory_names + 76, 27}, 4 }, + { {OperationResultCategory_names + 103, 17}, 9 }, + { {OperationResultCategory_names + 120, 27}, 3 }, + { {OperationResultCategory_names + 147, 22}, 8 }, + { {OperationResultCategory_names + 169, 21}, 6 }, + { {OperationResultCategory_names + 190, 16}, 1 }, + { {OperationResultCategory_names + 206, 16}, 0 }, +}; + +static const int OperationResultCategory_entries_by_number[] = { + 9, // 0 -> CATEGORY_UNKNOWN + 8, // 1 -> CATEGORY_SUCCESS + 0, // 2 -> CATEGORY_CLIENT_CANCELLATION + 5, // 3 -> CATEGORY_MEDIUM_UNAVAILABLE + 3, // 4 -> CATEGORY_DEVICE_STATE_ERROR + 1, // 5 -> CATEGORY_CLIENT_ERROR + 7, // 6 -> CATEGORY_NEARBY_ERROR + 2, // 7 -> CATEGORY_CONNECTIVITY_ERROR + 6, // 8 -> CATEGORY_MISCELLANEOUS + 4, // 9 -> CATEGORY_IO_ERROR +}; + +const std::string& OperationResultCategory_Name( + OperationResultCategory value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + OperationResultCategory_entries, + OperationResultCategory_entries_by_number, + 10, OperationResultCategory_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + OperationResultCategory_entries, + OperationResultCategory_entries_by_number, + 10, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + OperationResultCategory_strings[idx].get(); +} +bool OperationResultCategory_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, OperationResultCategory* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + OperationResultCategory_entries, 10, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} +bool OperationResultDetail_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 500: + case 501: + case 502: + case 503: + case 504: + case 505: + case 506: + case 507: + case 508: + case 509: + case 510: + case 511: + case 512: + case 513: + case 514: + case 515: + case 516: + case 517: + case 518: + case 519: + case 520: + case 521: + case 522: + case 523: + case 1000: + case 1001: + case 1002: + case 1003: + case 1004: + case 1500: + case 1501: + case 1502: + case 1503: + case 1504: + case 1505: + case 1506: + case 1507: + case 1508: + case 1509: + case 1510: + case 1511: + case 1512: + case 1513: + case 1514: + case 1515: + case 1516: + case 1517: + case 1518: + case 1519: + case 1520: + case 1521: + case 1522: + case 1523: + case 1524: + case 1525: + case 1526: + case 1527: + case 1528: + case 1529: + case 1530: + case 1531: + case 1532: + case 1533: + case 1534: + case 1535: + case 1536: + case 1537: + case 1538: + case 2000: + case 2001: + case 2002: + case 2003: + case 2004: + case 2005: + case 2006: + case 2007: + case 2008: + case 2009: + case 2010: + case 2011: + case 2012: + case 2013: + case 2014: + case 2015: + case 2016: + case 2500: + case 2501: + case 2502: + case 2503: + case 2504: + case 2505: + case 2506: + case 2507: + case 2508: + case 2509: + case 2510: + case 2511: + case 2512: + case 3000: + case 3001: + case 3002: + case 3003: + case 3004: + case 3005: + case 3006: + case 3007: + case 3008: + case 3009: + case 3010: + case 3011: + case 3012: + case 3013: + case 3014: + case 3500: + case 3501: + case 3502: + case 3503: + case 3504: + case 3505: + case 3506: + case 3507: + case 3508: + case 3509: + case 3510: + case 3511: + case 3512: + case 3513: + case 3514: + case 3515: + case 3516: + case 3517: + case 3518: + case 3519: + case 3520: + case 3521: + case 3522: + case 3523: + case 3524: + case 3525: + case 3526: + case 3527: + case 3528: + case 3529: + case 3530: + case 3531: + case 3532: + case 3533: + case 3534: + case 3535: + case 3536: + case 3537: + case 3538: + case 3539: + case 3540: + case 3541: + case 3542: + case 3543: + case 3544: + case 3545: + case 3546: + case 3547: + case 3548: + case 3549: + case 3550: + case 3551: + case 3552: + case 3553: + case 3554: + case 3555: + case 4500: + case 4501: + case 4502: + case 4503: + case 4504: + case 4505: + case 4506: + case 4507: + case 4508: + case 4509: + case 4510: + case 4511: + case 4512: + case 4513: + case 4514: + case 4515: + case 4516: + case 4517: + case 4518: + case 4519: + case 4520: + case 4521: + case 4522: + case 4523: + case 4524: + case 4525: + case 4526: + case 4527: + case 4528: + case 4529: + case 4530: + case 4531: + case 4532: + case 4533: + case 4534: + case 4535: + case 4536: + case 4537: + case 4538: + case 4539: + case 4540: + case 4541: + case 4542: + case 4543: + case 4544: + case 4545: + case 4546: + case 4547: + case 4548: + case 4549: + case 4550: + case 4551: + case 4552: + case 4553: + case 4554: + case 4555: + case 4556: + case 4557: + case 4558: + case 4559: + case 4560: + case 4561: + case 4562: + case 4563: + case 4564: + case 4565: + case 4566: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed OperationResultDetail_strings[238] = {}; + +static const char OperationResultDetail_names[] = + "CLIENT_CANCELLATION_BT_SERVER_SOCKET_CREATION" + "CLIENT_CANCELLATION_CANCEL_BLE_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_BT_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_INCOMING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_L2CAP_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_LAN_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_NFC_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_USB_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_WEB_RTC_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_WIFI_AWARE_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_WIFI_DIRECT_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_CANCEL_WIFI_HOTSPOT_OUTGOING_CONNECTION" + "CLIENT_CANCELLATION_LOCAL_CANCEL_PAYLOAD" + "CLIENT_CANCELLATION_LOCAL_DISCONNECT" + "CLIENT_CANCELLATION_REMOTE_CANCEL_PAYLOAD" + "CLIENT_CANCELLATION_REMOTE_DISCONNECT" + "CLIENT_CANCELLATION_REMOTE_IN_CANCELED_STATE" + "CLIENT_CANCELLATION_UPGRADE_CANCELED_BY_REMOTE" + "CLIENT_CANCELLATION_WEB_RTC_SERVER_SOCKET_CREATION" + "CLIENT_CANCELLATION_WIFI_AWARE_SERVER_SOCKET_CREATION" + "CLIENT_CANCELLATION_WIFI_DIRECT_SERVER_SOCKET_CREATION" + "CLIENT_CANCELLATION_WIFI_HOTSPOT_SERVER_SOCKET_CREATION" + "CLIENT_CANCELLATION_WIFI_LAN_SERVER_SOCKET_CREATION" + "CLIENT_DUPLICATE_ACCEPTING_BLE_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_ACCEPTING_BT_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_ACCEPTING_L2CAP_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_ACCEPTING_LAN_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_ACCEPTING_NFC_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_ACCEPTING_USB_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_ACCEPTING_WEB_RTC_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_ACCEPTING_WIFI_AWARE_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_ACCEPTING_WIFI_DIRECT_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_ACCEPTING_WIFI_HOTSPOT_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_WIFI_AWARE_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_WIFI_AWARE_SUBSCRIBING_REQUEST" + "CLIENT_DUPLICATE_WIFI_DIRECT_CONNECTION_REQUEST" + "CLIENT_DUPLICATE_WIFI_HOTSPOT_CONNECTION_REQUEST" + "CLIENT_UNSUPPORTED_USB_TO_BE_UPGRADE_MEDIUM" + "CLIENT_WIFI_DIRECT_ALREADY_HOSTING_DIRECT_GROUP_FOR_THIS_CLIENT" + "CLIENT_WIFI_HOTSPOT_ALREADY_HOSTING_HOTSPOT_FOR_THIS_CLIENT" + "CONNECTIVITY_BLE_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_BLE_CREATE_GATT_CONNECTION_FAILURE" + "CONNECTIVITY_BLE_SERVER_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_BLUETOOTH_DEVICE_OBTAIN_FAILURE" + "CONNECTIVITY_BLUETOOTH_INVALID_CREDENTIAL" + "CONNECTIVITY_BT_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_BT_SERVER_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_BT_SERVER_SOCKET_CREATION_SECURITY_EXCEPTION_FAILURE" + "CONNECTIVITY_GATT_SERVER_OPEN_FAILURE" + "CONNECTIVITY_GENERIC_PAYLOAD_SENT_ERROR" + "CONNECTIVITY_GENERIC_WRITE_CLIENT_INTRODUCTION_ACK_IO_ERROR" + "CONNECTIVITY_GENERIC_WRITING_CHANNEL_IO_ERROR" + "CONNECTIVITY_L2CAP_CLIENT_OBTAIN_FAIURE" + "CONNECTIVITY_L2CAP_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_L2CAP_DATA_CONNECTION_FAILURE" + "CONNECTIVITY_L2CAP_SERVER_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_L2CAP_SERVER_SOCKET_CREATION_SECURITY_EXCEPTION_FAILURE" + "CONNECTIVITY_LAN_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_LAN_GET_NETWORK_INTERFACES_FAILURE" + "CONNECTIVITY_LAN_SERVER_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_LAN_UNREACHABLE" + "CONNECTIVITY_NFC_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_NFC_SERVER_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_USB_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_WEB_RTC_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_WEB_RTC_CONNECT_TO_TACHYON_FAILURE" + "CONNECTIVITY_WEB_RTC_INVALID_CREDENTIAL" + "CONNECTIVITY_WEB_RTC_SERVER_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_WIFI_AWARE_ATTACH_FAILURE" + "CONNECTIVITY_WIFI_AWARE_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_WIFI_AWARE_DISCOVERED_PEER_NULL" + "CONNECTIVITY_WIFI_AWARE_GET_REMOTE_IP_ADDRESS_FAILURE" + "CONNECTIVITY_WIFI_AWARE_GET_REMOTE_IP_FRAME_FAILURE" + "CONNECTIVITY_WIFI_AWARE_INVALID_CREDENTIAL" + "CONNECTIVITY_WIFI_AWARE_L2MESSAGE_NETWORK_AVAILABLE_FRAME_NULL" + "CONNECTIVITY_WIFI_AWARE_L2MESSAGE_SEND_HOST_NETWORK_FRAME_FAILURE" + "CONNECTIVITY_WIFI_AWARE_SERVER_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_WIFI_AWARE_UPDATE_PUBLISH_FAILURE" + "CONNECTIVITY_WIFI_DIRECT_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_WIFI_DIRECT_GET_NETWORK_INTERFACES_FAILURE" + "CONNECTIVITY_WIFI_DIRECT_INCONSISTENT_HOSTED_WIFI_BAND" + "CONNECTIVITY_WIFI_DIRECT_INVALID_CREDENTIAL" + "CONNECTIVITY_WIFI_DIRECT_P2P_CHANNEL_INITIALIZE_FAILURE" + "CONNECTIVITY_WIFI_DIRECT_P2P_GROUP_CREATION_FAILURE" + "CONNECTIVITY_WIFI_DIRECT_SERVER_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_WIFI_HOTSPOT_CLIENT_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_WIFI_HOTSPOT_GET_NETWORK_INTERFACES_FAILURE" + "CONNECTIVITY_WIFI_HOTSPOT_INCONSISTENT_HOSTED_WIFI_BAND" + "CONNECTIVITY_WIFI_HOTSPOT_INVALID_CREDENTIAL" + "CONNECTIVITY_WIFI_HOTSPOT_LOHS_CREATION_FAILURE" + "CONNECTIVITY_WIFI_HOTSPOT_P2P_CHANNEL_INITIALIZE_FAILURE" + "CONNECTIVITY_WIFI_HOTSPOT_P2P_GROUP_CREATION_FAILURE" + "CONNECTIVITY_WIFI_HOTSPOT_SERVER_SOCKET_CREATION_FAILURE" + "CONNECTIVITY_WIFI_HOTSPOT_SOFT_AP_CREATION_FAILURE" + "CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL" + "CONNECTIVITY_WIFI_LAN_IP_ADDRESS_ERROR" + "DETAIL_SUCCESS" + "DETAIL_UNKNOWN" + "DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS" + "DEVICE_STATE_ERROR_USER_HOTSPOT_ENABLED" + "DEVICE_STATE_LOCATION_DISABLED" + "DEVICE_STATE_RADIO_DISABLING_FAILURE" + "DEVICE_STATE_RADIO_ENABLING_FAILURE" + "IO_ENDPOINT_IO_ERROR_ON_BLE" + "IO_ENDPOINT_IO_ERROR_ON_BT" + "IO_ENDPOINT_IO_ERROR_ON_L2CAP" + "IO_ENDPOINT_IO_ERROR_ON_LAN" + "IO_ENDPOINT_IO_ERROR_ON_NFC" + "IO_ENDPOINT_IO_ERROR_ON_USB" + "IO_ENDPOINT_IO_ERROR_ON_WEB_RTC" + "IO_ENDPOINT_IO_ERROR_ON_WIFI_AWARE" + "IO_ENDPOINT_IO_ERROR_ON_WIFI_DIRECT" + "IO_ENDPOINT_IO_ERROR_ON_WIFI_HOTSPOT" + "IO_FILE_OPENING_ERROR" + "IO_FILE_READING_ERROR" + "IO_FILE_WRITING_ERROR" + "IO_FOLDER_CREATION_ERROR" + "IO_STREAM_CREATE_PIPE_FAILURE" + "MEDIUM_UNAVAILABLE_ALREADY_HAVE_A_WIFI_DIRECT_GROUP" + "MEDIUM_UNAVAILABLE_ALREADY_HOSTING_HOTSPOT_FOR_OTHER_CLIENTS" + "MEDIUM_UNAVAILABLE_BLE_NC_LOGICAL_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_BLE_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_BLUETOOTH_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_BT_NC_LOGICAL_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_DIRECT_HOTSPOT_NOT_SUPPORT" + "MEDIUM_UNAVAILABLE_L2CAP_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_LAN_NC_LOGICAL_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_LAN_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_DISRUPTIVE_FALSE" + "MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT" + "MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT_5G" + "MEDIUM_UNAVAILABLE_NFC_NC_LOGICAL_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_NFC_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_REJECT_L2CAP_ON_GATT_MULTIPLEX_CONNECTION" + "MEDIUM_UNAVAILABLE_SOFT_AP_DISRUPTIVE_FALSE" + "MEDIUM_UNAVAILABLE_SOFT_AP_NOT_SUPPORT" + "MEDIUM_UNAVAILABLE_UPGRADE_ON_SAME_MEDIUM" + "MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BLE_LOW_QUALITY_MEDIUMS" + "MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BT_LOW_QUALITY_MEDIUMS" + "MEDIUM_UNAVAILABLE_UPGRADE_SKIP_L2CAP_LOW_QUALITY_MEDIUMS" + "MEDIUM_UNAVAILABLE_UPGRADE_SKIP_LAN_LOW_QUALITY_MEDIUMS" + "MEDIUM_UNAVAILABLE_UPGRADE_SKIP_USB_LOW_QUALITY_MEDIUMS" + "MEDIUM_UNAVAILABLE_UPGRADE_SKIP_WEB_RTC_LOW_QUALITY_MEDIUMS" + "MEDIUM_UNAVAILABLE_USB_NC_LOGICAL_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_USB_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WEB_RTC_NC_LOGICAL_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WEB_RTC_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WEB_RTC_NO_INTERNET" + "MEDIUM_UNAVAILABLE_WIFI_AWARE_NC_LOGICAL_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WIFI_AWARE_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WIFI_AWARE_RESOURCE_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WIFI_DIRECT_NC_LOGICAL_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WIFI_DIRECT_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WIFI_DIRECT_P2P_RESOURCE_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NC_LOGICAL_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NOT_AVAILABLE" + "MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_P2P_RESOURCE_NOT_AVAILABLE" + "MISCELLEANEOUS_BLE_SYSTEM_SERVICE_NULL" + "MISCELLEANEOUS_BLUETOOTH_MAC_ADDRESS_NULL" + "MISCELLEANEOUS_BT_NOT_ACCEPTING_CONNECTION_FOR_WORK_PROFILE" + "MISCELLEANEOUS_BT_SYSTEM_SERVICE_NULL" + "MISCELLEANEOUS_L2CAP_SYSTEM_SERVICE_NULL" + "MISCELLEANEOUS_MOVE_TO_NEW_MEDIUM" + "MISCELLEANEOUS_WEB_RTC_GET_DROIDGUARD_RESULT_FAILURE" + "MISCELLEANEOUS_WEB_RTC_TACHYON_SIGNALING_MESSENGER_NULL" + "MISCELLEANEOUS_WIFI_AWARE_SYSTEM_SERVICE_NULL" + "MISCELLEANEOUS_WIFI_DIRECT_SYSTEM_SERVICE_NULL" + "MISCELLEANEOUS_WIFI_HOTSPOT_SOFT_AP_BLOCKED_BY_PROVISION" + "MISCELLEANEOUS_WIFI_HOTSPOT_SYSTEM_SERVICE_NULL" + "MISCELLEANEOUS_WIFI_LAN_SYSTEM_SERVICE_NULL" + "NEARBY_BLE_ADVERTISEMENT_MAPPING_TO_MAC_ERROR" + "NEARBY_BLE_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_BLE_GATT_ADVERTISEMENT_NULL_FOR_CONNECTION" + "NEARBY_BLE_GATT_NULL_CALLBACK" + "NEARBY_BLE_OPERATION_REGISTERED_FAILED" + "NEARBY_BLUETOOTH_MAC_ADDRESS_INVALID_FOR_CONNECT" + "NEARBY_BT_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_BT_MULTIPLEX_SOCKET_DISABLED" + "NEARBY_BT_NULL_CALLBACK" + "NEARBY_BT_OPERATION_REGISTERED_FAILED" + "NEARBY_BT_VIRTUAL_SOCKET_CREATION_FAILURE" + "NEARBY_GENERIC_CONNECTION_CLOSED" + "NEARBY_GENERIC_ENDPOINT_UNENCRYPTED" + "NEARBY_GENERIC_INCOMING_PAYLOAD_NOT_DATA_TYPE" + "NEARBY_GENERIC_NEW_ENDPOINT_CHANNEL_NULL" + "NEARBY_GENERIC_OLD_ENDPOINT_CHANNEL_NULL" + "NEARBY_GENERIC_OUTGOING_PAYLOAD_CREATION_FAILURE" + "NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_EVENT_TYPE_ERROR" + "NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FORMAT_ERROR" + "NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FRAME_TYPE_ERROR" + "NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_EVENT_TYPE_ERROR" + "NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FORMAT_ERROR" + "NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FRAME_TYPE_ERROR" + "NEARBY_GENERIC_REMOTE_ENDPOINT_STATUS_ERROR" + "NEARBY_GENERIC_REMOTE_REPORT_PAYLOADS_ERROR" + "NEARBY_GENERIC_REMOTE_UPGRADE_FAILURE" + "NEARBY_GENERIC_SEND_PAYLOAD_EXECUTOR_NULL" + "NEARBY_L2CAP_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_L2CAP_NULL_CALLBACK" + "NEARBY_L2CAP_OPERATION_REGISTERED_FAILED" + "NEARBY_L2CAP_PSM_NOT_POSITIVE" + "NEARBY_LAN_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_LAN_MULTIPLEX_SOCKET_DISABLED" + "NEARBY_LAN_NULL_CALLBACK" + "NEARBY_LAN_OPERATION_REGISTERED_FAILED" + "NEARBY_LAN_VIRTUAL_SOCKET_CREATION_FAILURE" + "NEARBY_NFC_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_NFC_NULL_CALLBACK" + "NEARBY_USB_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_USB_NULL_CALLBACK" + "NEARBY_WEB_RTC_CONNECTION_FLOW_NULL" + "NEARBY_WEB_RTC_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_WEB_RTC_NULL_CALLBACK" + "NEARBY_WEB_RTC_OPERATION_REGISTERED_FAILED" + "NEARBY_WIFI_AWARE_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_WIFI_AWARE_NULL_CALLBACK" + "NEARBY_WIFI_AWARE_OPERATION_REGISTERED_FAILED" + "NEARBY_WIFI_DIRECT_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_WIFI_DIRECT_HOST_ON_SRD_CHANNELS" + "NEARBY_WIFI_DIRECT_NO_GROUP_FOR_LISTENING" + "NEARBY_WIFI_DIRECT_NULL_CALLBACK" + "NEARBY_WIFI_DIRECT_NULL_PASSWORD" + "NEARBY_WIFI_DIRECT_NULL_SSID" + "NEARBY_WIFI_DIRECT_OPERATION_REGISTERED_FAILED" + "NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_2G_BUT_AP_5G" + "NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_5G_BUT_AP_2G" + "NEARBY_WIFI_HOTSPOT_CLIENT_OPERATION_REGISTERED_FAILED" + "NEARBY_WIFI_HOTSPOT_DIRECT_OPERATION_REGISTERED_FAILED" + "NEARBY_WIFI_HOTSPOT_ENDPOINT_CHANNEL_CREATION_FAILURE" + "NEARBY_WIFI_HOTSPOT_HOST_ON_SRD_CHANNELS" + "NEARBY_WIFI_HOTSPOT_LOHS_OPERATION_REGISTERED_FAILED" + "NEARBY_WIFI_HOTSPOT_NO_HOTSPOT_FOR_LISTENING" + "NEARBY_WIFI_HOTSPOT_NULL_CALLBACK" + "NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_2G_BUT_AP_5G" + "NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_5G_BUT_AP_2G" + "NEARBY_WIFI_HOTSPOT_SOFT_AP_OPERATION_REGISTERED_FAILED" + "NEARBY_WIFI_LAN_IP_ADDRESS_ERROR"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry OperationResultDetail_entries[] = { + { {OperationResultDetail_names + 0, 45}, 519 }, + { {OperationResultDetail_names + 45, 50}, 504 }, + { {OperationResultDetail_names + 95, 49}, 505 }, + { {OperationResultDetail_names + 144, 46}, 515 }, + { {OperationResultDetail_names + 190, 52}, 506 }, + { {OperationResultDetail_names + 242, 50}, 507 }, + { {OperationResultDetail_names + 292, 50}, 508 }, + { {OperationResultDetail_names + 342, 46}, 514 }, + { {OperationResultDetail_names + 388, 50}, 509 }, + { {OperationResultDetail_names + 438, 54}, 513 }, + { {OperationResultDetail_names + 492, 57}, 510 }, + { {OperationResultDetail_names + 549, 58}, 511 }, + { {OperationResultDetail_names + 607, 59}, 512 }, + { {OperationResultDetail_names + 666, 40}, 501 }, + { {OperationResultDetail_names + 706, 36}, 522 }, + { {OperationResultDetail_names + 742, 41}, 502 }, + { {OperationResultDetail_names + 783, 37}, 523 }, + { {OperationResultDetail_names + 820, 44}, 500 }, + { {OperationResultDetail_names + 864, 46}, 503 }, + { {OperationResultDetail_names + 910, 50}, 520 }, + { {OperationResultDetail_names + 960, 53}, 516 }, + { {OperationResultDetail_names + 1013, 54}, 517 }, + { {OperationResultDetail_names + 1067, 55}, 518 }, + { {OperationResultDetail_names + 1122, 51}, 521 }, + { {OperationResultDetail_names + 1173, 49}, 2002 }, + { {OperationResultDetail_names + 1222, 48}, 2004 }, + { {OperationResultDetail_names + 1270, 51}, 2003 }, + { {OperationResultDetail_names + 1321, 49}, 2005 }, + { {OperationResultDetail_names + 1370, 49}, 2006 }, + { {OperationResultDetail_names + 1419, 49}, 2011 }, + { {OperationResultDetail_names + 1468, 53}, 2007 }, + { {OperationResultDetail_names + 1521, 56}, 2008 }, + { {OperationResultDetail_names + 1577, 57}, 2010 }, + { {OperationResultDetail_names + 1634, 58}, 2009 }, + { {OperationResultDetail_names + 1692, 46}, 2012 }, + { {OperationResultDetail_names + 1738, 47}, 2015 }, + { {OperationResultDetail_names + 1785, 47}, 2013 }, + { {OperationResultDetail_names + 1832, 48}, 2014 }, + { {OperationResultDetail_names + 1880, 43}, 2016 }, + { {OperationResultDetail_names + 1923, 63}, 2000 }, + { {OperationResultDetail_names + 1986, 59}, 2001 }, + { {OperationResultDetail_names + 2045, 47}, 3502 }, + { {OperationResultDetail_names + 2092, 47}, 3513 }, + { {OperationResultDetail_names + 2139, 47}, 3539 }, + { {OperationResultDetail_names + 2186, 44}, 3501 }, + { {OperationResultDetail_names + 2230, 41}, 3518 }, + { {OperationResultDetail_names + 2271, 46}, 3504 }, + { {OperationResultDetail_names + 2317, 46}, 3541 }, + { {OperationResultDetail_names + 2363, 65}, 3555 }, + { {OperationResultDetail_names + 2428, 37}, 3538 }, + { {OperationResultDetail_names + 2465, 39}, 3553 }, + { {OperationResultDetail_names + 2504, 59}, 3551 }, + { {OperationResultDetail_names + 2563, 45}, 3550 }, + { {OperationResultDetail_names + 2608, 39}, 3525 }, + { {OperationResultDetail_names + 2647, 49}, 3503 }, + { {OperationResultDetail_names + 2696, 42}, 3526 }, + { {OperationResultDetail_names + 2738, 49}, 3540 }, + { {OperationResultDetail_names + 2787, 68}, 3554 }, + { {OperationResultDetail_names + 2855, 47}, 3505 }, + { {OperationResultDetail_names + 2902, 47}, 3531 }, + { {OperationResultDetail_names + 2949, 47}, 3542 }, + { {OperationResultDetail_names + 2996, 28}, 3529 }, + { {OperationResultDetail_names + 3024, 47}, 3506 }, + { {OperationResultDetail_names + 3071, 47}, 3547 }, + { {OperationResultDetail_names + 3118, 47}, 3511 }, + { {OperationResultDetail_names + 3165, 51}, 3507 }, + { {OperationResultDetail_names + 3216, 47}, 3512 }, + { {OperationResultDetail_names + 3263, 39}, 3523 }, + { {OperationResultDetail_names + 3302, 51}, 3543 }, + { {OperationResultDetail_names + 3353, 38}, 3500 }, + { {OperationResultDetail_names + 3391, 54}, 3508 }, + { {OperationResultDetail_names + 3445, 44}, 3552 }, + { {OperationResultDetail_names + 3489, 53}, 3515 }, + { {OperationResultDetail_names + 3542, 51}, 3514 }, + { {OperationResultDetail_names + 3593, 42}, 3522 }, + { {OperationResultDetail_names + 3635, 62}, 3527 }, + { {OperationResultDetail_names + 3697, 65}, 3528 }, + { {OperationResultDetail_names + 3762, 54}, 3544 }, + { {OperationResultDetail_names + 3816, 46}, 3549 }, + { {OperationResultDetail_names + 3862, 55}, 3510 }, + { {OperationResultDetail_names + 3917, 55}, 3532 }, + { {OperationResultDetail_names + 3972, 54}, 3516 }, + { {OperationResultDetail_names + 4026, 43}, 3520 }, + { {OperationResultDetail_names + 4069, 55}, 3535 }, + { {OperationResultDetail_names + 4124, 51}, 3537 }, + { {OperationResultDetail_names + 4175, 55}, 3546 }, + { {OperationResultDetail_names + 4230, 56}, 3509 }, + { {OperationResultDetail_names + 4286, 56}, 3533 }, + { {OperationResultDetail_names + 4342, 55}, 3517 }, + { {OperationResultDetail_names + 4397, 44}, 3521 }, + { {OperationResultDetail_names + 4441, 47}, 3530 }, + { {OperationResultDetail_names + 4488, 56}, 3534 }, + { {OperationResultDetail_names + 4544, 52}, 3536 }, + { {OperationResultDetail_names + 4596, 56}, 3545 }, + { {OperationResultDetail_names + 4652, 50}, 3548 }, + { {OperationResultDetail_names + 4702, 40}, 3519 }, + { {OperationResultDetail_names + 4742, 38}, 3524 }, + { {OperationResultDetail_names + 4780, 14}, 1 }, + { {OperationResultDetail_names + 4794, 14}, 0 }, + { {OperationResultDetail_names + 4808, 46}, 1000 }, + { {OperationResultDetail_names + 4854, 39}, 1001 }, + { {OperationResultDetail_names + 4893, 30}, 1002 }, + { {OperationResultDetail_names + 4923, 36}, 1003 }, + { {OperationResultDetail_names + 4959, 35}, 1004 }, + { {OperationResultDetail_names + 4994, 27}, 3005 }, + { {OperationResultDetail_names + 5021, 26}, 3007 }, + { {OperationResultDetail_names + 5047, 29}, 3006 }, + { {OperationResultDetail_names + 5076, 27}, 3009 }, + { {OperationResultDetail_names + 5103, 27}, 3013 }, + { {OperationResultDetail_names + 5130, 27}, 3014 }, + { {OperationResultDetail_names + 5157, 31}, 3008 }, + { {OperationResultDetail_names + 5188, 34}, 3012 }, + { {OperationResultDetail_names + 5222, 35}, 3010 }, + { {OperationResultDetail_names + 5257, 36}, 3011 }, + { {OperationResultDetail_names + 5293, 21}, 3000 }, + { {OperationResultDetail_names + 5314, 21}, 3001 }, + { {OperationResultDetail_names + 5335, 21}, 3002 }, + { {OperationResultDetail_names + 5356, 24}, 3003 }, + { {OperationResultDetail_names + 5380, 29}, 3004 }, + { {OperationResultDetail_names + 5409, 51}, 1534 }, + { {OperationResultDetail_names + 5460, 60}, 1535 }, + { {OperationResultDetail_names + 5520, 47}, 1515 }, + { {OperationResultDetail_names + 5567, 36}, 1505 }, + { {OperationResultDetail_names + 5603, 42}, 1507 }, + { {OperationResultDetail_names + 5645, 46}, 1516 }, + { {OperationResultDetail_names + 5691, 45}, 1501 }, + { {OperationResultDetail_names + 5736, 38}, 1506 }, + { {OperationResultDetail_names + 5774, 47}, 1517 }, + { {OperationResultDetail_names + 5821, 36}, 1513 }, + { {OperationResultDetail_names + 5857, 54}, 1532 }, + { {OperationResultDetail_names + 5911, 49}, 1503 }, + { {OperationResultDetail_names + 5960, 52}, 1504 }, + { {OperationResultDetail_names + 6012, 47}, 1518 }, + { {OperationResultDetail_names + 6059, 36}, 1512 }, + { {OperationResultDetail_names + 6095, 60}, 1536 }, + { {OperationResultDetail_names + 6155, 43}, 1533 }, + { {OperationResultDetail_names + 6198, 38}, 1502 }, + { {OperationResultDetail_names + 6236, 41}, 1537 }, + { {OperationResultDetail_names + 6277, 55}, 1526 }, + { {OperationResultDetail_names + 6332, 54}, 1530 }, + { {OperationResultDetail_names + 6386, 57}, 1527 }, + { {OperationResultDetail_names + 6443, 55}, 1529 }, + { {OperationResultDetail_names + 6498, 55}, 1531 }, + { {OperationResultDetail_names + 6553, 59}, 1528 }, + { {OperationResultDetail_names + 6612, 47}, 1519 }, + { {OperationResultDetail_names + 6659, 36}, 1514 }, + { {OperationResultDetail_names + 6695, 51}, 1520 }, + { {OperationResultDetail_names + 6746, 40}, 1508 }, + { {OperationResultDetail_names + 6786, 38}, 1538 }, + { {OperationResultDetail_names + 6824, 54}, 1521 }, + { {OperationResultDetail_names + 6878, 43}, 1509 }, + { {OperationResultDetail_names + 6921, 52}, 1500 }, + { {OperationResultDetail_names + 6973, 55}, 1523 }, + { {OperationResultDetail_names + 7028, 44}, 1511 }, + { {OperationResultDetail_names + 7072, 57}, 1525 }, + { {OperationResultDetail_names + 7129, 56}, 1522 }, + { {OperationResultDetail_names + 7185, 45}, 1510 }, + { {OperationResultDetail_names + 7230, 58}, 1524 }, + { {OperationResultDetail_names + 7288, 38}, 2503 }, + { {OperationResultDetail_names + 7326, 41}, 2500 }, + { {OperationResultDetail_names + 7367, 59}, 2510 }, + { {OperationResultDetail_names + 7426, 37}, 2505 }, + { {OperationResultDetail_names + 7463, 40}, 2504 }, + { {OperationResultDetail_names + 7503, 33}, 2501 }, + { {OperationResultDetail_names + 7536, 52}, 2511 }, + { {OperationResultDetail_names + 7588, 55}, 2512 }, + { {OperationResultDetail_names + 7643, 45}, 2506 }, + { {OperationResultDetail_names + 7688, 46}, 2507 }, + { {OperationResultDetail_names + 7734, 56}, 2502 }, + { {OperationResultDetail_names + 7790, 47}, 2509 }, + { {OperationResultDetail_names + 7837, 43}, 2508 }, + { {OperationResultDetail_names + 7880, 45}, 4500 }, + { {OperationResultDetail_names + 7925, 44}, 4504 }, + { {OperationResultDetail_names + 7969, 49}, 4515 }, + { {OperationResultDetail_names + 8018, 29}, 4518 }, + { {OperationResultDetail_names + 8047, 38}, 4536 }, + { {OperationResultDetail_names + 8085, 48}, 4501 }, + { {OperationResultDetail_names + 8133, 43}, 4506 }, + { {OperationResultDetail_names + 8176, 35}, 4530 }, + { {OperationResultDetail_names + 8211, 23}, 4520 }, + { {OperationResultDetail_names + 8234, 37}, 4538 }, + { {OperationResultDetail_names + 8271, 41}, 4563 }, + { {OperationResultDetail_names + 8312, 32}, 4503 }, + { {OperationResultDetail_names + 8344, 35}, 4514 }, + { {OperationResultDetail_names + 8379, 45}, 4552 }, + { {OperationResultDetail_names + 8424, 40}, 4532 }, + { {OperationResultDetail_names + 8464, 40}, 4535 }, + { {OperationResultDetail_names + 8504, 48}, 4547 }, + { {OperationResultDetail_names + 8552, 60}, 4556 }, + { {OperationResultDetail_names + 8612, 56}, 4558 }, + { {OperationResultDetail_names + 8668, 60}, 4557 }, + { {OperationResultDetail_names + 8728, 56}, 4553 }, + { {OperationResultDetail_names + 8784, 52}, 4555 }, + { {OperationResultDetail_names + 8836, 56}, 4554 }, + { {OperationResultDetail_names + 8892, 43}, 4559 }, + { {OperationResultDetail_names + 8935, 43}, 4560 }, + { {OperationResultDetail_names + 8978, 37}, 4561 }, + { {OperationResultDetail_names + 9015, 41}, 4562 }, + { {OperationResultDetail_names + 9056, 46}, 4505 }, + { {OperationResultDetail_names + 9102, 26}, 4519 }, + { {OperationResultDetail_names + 9128, 40}, 4537 }, + { {OperationResultDetail_names + 9168, 29}, 4566 }, + { {OperationResultDetail_names + 9197, 44}, 4507 }, + { {OperationResultDetail_names + 9241, 36}, 4531 }, + { {OperationResultDetail_names + 9277, 24}, 4525 }, + { {OperationResultDetail_names + 9301, 38}, 4539 }, + { {OperationResultDetail_names + 9339, 42}, 4564 }, + { {OperationResultDetail_names + 9381, 44}, 4508 }, + { {OperationResultDetail_names + 9425, 24}, 4522 }, + { {OperationResultDetail_names + 9449, 44}, 4513 }, + { {OperationResultDetail_names + 9493, 24}, 4521 }, + { {OperationResultDetail_names + 9517, 35}, 4502 }, + { {OperationResultDetail_names + 9552, 48}, 4512 }, + { {OperationResultDetail_names + 9600, 28}, 4524 }, + { {OperationResultDetail_names + 9628, 42}, 4540 }, + { {OperationResultDetail_names + 9670, 51}, 4509 }, + { {OperationResultDetail_names + 9721, 31}, 4523 }, + { {OperationResultDetail_names + 9752, 45}, 4541 }, + { {OperationResultDetail_names + 9797, 52}, 4511 }, + { {OperationResultDetail_names + 9849, 39}, 4516 }, + { {OperationResultDetail_names + 9888, 41}, 4533 }, + { {OperationResultDetail_names + 9929, 32}, 4527 }, + { {OperationResultDetail_names + 9961, 32}, 4529 }, + { {OperationResultDetail_names + 9993, 28}, 4528 }, + { {OperationResultDetail_names + 10021, 46}, 4546 }, + { {OperationResultDetail_names + 10067, 48}, 4549 }, + { {OperationResultDetail_names + 10115, 48}, 4551 }, + { {OperationResultDetail_names + 10163, 54}, 4545 }, + { {OperationResultDetail_names + 10217, 54}, 4542 }, + { {OperationResultDetail_names + 10271, 53}, 4510 }, + { {OperationResultDetail_names + 10324, 40}, 4517 }, + { {OperationResultDetail_names + 10364, 52}, 4544 }, + { {OperationResultDetail_names + 10416, 44}, 4534 }, + { {OperationResultDetail_names + 10460, 33}, 4526 }, + { {OperationResultDetail_names + 10493, 49}, 4548 }, + { {OperationResultDetail_names + 10542, 49}, 4550 }, + { {OperationResultDetail_names + 10591, 55}, 4543 }, + { {OperationResultDetail_names + 10646, 32}, 4565 }, +}; + +static const int OperationResultDetail_entries_by_number[] = { + 98, // 0 -> DETAIL_UNKNOWN + 97, // 1 -> DETAIL_SUCCESS + 17, // 500 -> CLIENT_CANCELLATION_REMOTE_IN_CANCELED_STATE + 13, // 501 -> CLIENT_CANCELLATION_LOCAL_CANCEL_PAYLOAD + 15, // 502 -> CLIENT_CANCELLATION_REMOTE_CANCEL_PAYLOAD + 18, // 503 -> CLIENT_CANCELLATION_UPGRADE_CANCELED_BY_REMOTE + 1, // 504 -> CLIENT_CANCELLATION_CANCEL_BLE_OUTGOING_CONNECTION + 2, // 505 -> CLIENT_CANCELLATION_CANCEL_BT_OUTGOING_CONNECTION + 4, // 506 -> CLIENT_CANCELLATION_CANCEL_L2CAP_OUTGOING_CONNECTION + 5, // 507 -> CLIENT_CANCELLATION_CANCEL_LAN_OUTGOING_CONNECTION + 6, // 508 -> CLIENT_CANCELLATION_CANCEL_NFC_OUTGOING_CONNECTION + 8, // 509 -> CLIENT_CANCELLATION_CANCEL_USB_OUTGOING_CONNECTION + 10, // 510 -> CLIENT_CANCELLATION_CANCEL_WIFI_AWARE_OUTGOING_CONNECTION + 11, // 511 -> CLIENT_CANCELLATION_CANCEL_WIFI_DIRECT_OUTGOING_CONNECTION + 12, // 512 -> CLIENT_CANCELLATION_CANCEL_WIFI_HOTSPOT_OUTGOING_CONNECTION + 9, // 513 -> CLIENT_CANCELLATION_CANCEL_WEB_RTC_OUTGOING_CONNECTION + 7, // 514 -> CLIENT_CANCELLATION_CANCEL_OUTGOING_CONNECTION + 3, // 515 -> CLIENT_CANCELLATION_CANCEL_INCOMING_CONNECTION + 20, // 516 -> CLIENT_CANCELLATION_WIFI_AWARE_SERVER_SOCKET_CREATION + 21, // 517 -> CLIENT_CANCELLATION_WIFI_DIRECT_SERVER_SOCKET_CREATION + 22, // 518 -> CLIENT_CANCELLATION_WIFI_HOTSPOT_SERVER_SOCKET_CREATION + 0, // 519 -> CLIENT_CANCELLATION_BT_SERVER_SOCKET_CREATION + 19, // 520 -> CLIENT_CANCELLATION_WEB_RTC_SERVER_SOCKET_CREATION + 23, // 521 -> CLIENT_CANCELLATION_WIFI_LAN_SERVER_SOCKET_CREATION + 14, // 522 -> CLIENT_CANCELLATION_LOCAL_DISCONNECT + 16, // 523 -> CLIENT_CANCELLATION_REMOTE_DISCONNECT + 99, // 1000 -> DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS + 100, // 1001 -> DEVICE_STATE_ERROR_USER_HOTSPOT_ENABLED + 101, // 1002 -> DEVICE_STATE_LOCATION_DISABLED + 102, // 1003 -> DEVICE_STATE_RADIO_DISABLING_FAILURE + 103, // 1004 -> DEVICE_STATE_RADIO_ENABLING_FAILURE + 151, // 1500 -> MEDIUM_UNAVAILABLE_WIFI_AWARE_RESOURCE_NOT_AVAILABLE + 125, // 1501 -> MEDIUM_UNAVAILABLE_DIRECT_HOTSPOT_NOT_SUPPORT + 136, // 1502 -> MEDIUM_UNAVAILABLE_SOFT_AP_NOT_SUPPORT + 130, // 1503 -> MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT + 131, // 1504 -> MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT_5G + 122, // 1505 -> MEDIUM_UNAVAILABLE_BLE_NOT_AVAILABLE + 126, // 1506 -> MEDIUM_UNAVAILABLE_L2CAP_NOT_AVAILABLE + 123, // 1507 -> MEDIUM_UNAVAILABLE_BLUETOOTH_NOT_AVAILABLE + 147, // 1508 -> MEDIUM_UNAVAILABLE_WEB_RTC_NOT_AVAILABLE + 150, // 1509 -> MEDIUM_UNAVAILABLE_WIFI_AWARE_NOT_AVAILABLE + 156, // 1510 -> MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NOT_AVAILABLE + 153, // 1511 -> MEDIUM_UNAVAILABLE_WIFI_DIRECT_NOT_AVAILABLE + 133, // 1512 -> MEDIUM_UNAVAILABLE_NFC_NOT_AVAILABLE + 128, // 1513 -> MEDIUM_UNAVAILABLE_LAN_NOT_AVAILABLE + 145, // 1514 -> MEDIUM_UNAVAILABLE_USB_NOT_AVAILABLE + 121, // 1515 -> MEDIUM_UNAVAILABLE_BLE_NC_LOGICAL_NOT_AVAILABLE + 124, // 1516 -> MEDIUM_UNAVAILABLE_BT_NC_LOGICAL_NOT_AVAILABLE + 127, // 1517 -> MEDIUM_UNAVAILABLE_LAN_NC_LOGICAL_NOT_AVAILABLE + 132, // 1518 -> MEDIUM_UNAVAILABLE_NFC_NC_LOGICAL_NOT_AVAILABLE + 144, // 1519 -> MEDIUM_UNAVAILABLE_USB_NC_LOGICAL_NOT_AVAILABLE + 146, // 1520 -> MEDIUM_UNAVAILABLE_WEB_RTC_NC_LOGICAL_NOT_AVAILABLE + 149, // 1521 -> MEDIUM_UNAVAILABLE_WIFI_AWARE_NC_LOGICAL_NOT_AVAILABLE + 155, // 1522 -> MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NC_LOGICAL_NOT_AVAILABLE + 152, // 1523 -> MEDIUM_UNAVAILABLE_WIFI_DIRECT_NC_LOGICAL_NOT_AVAILABLE + 157, // 1524 -> MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_P2P_RESOURCE_NOT_AVAILABLE + 154, // 1525 -> MEDIUM_UNAVAILABLE_WIFI_DIRECT_P2P_RESOURCE_NOT_AVAILABLE + 138, // 1526 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BLE_LOW_QUALITY_MEDIUMS + 140, // 1527 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_L2CAP_LOW_QUALITY_MEDIUMS + 143, // 1528 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_WEB_RTC_LOW_QUALITY_MEDIUMS + 141, // 1529 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_LAN_LOW_QUALITY_MEDIUMS + 139, // 1530 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BT_LOW_QUALITY_MEDIUMS + 142, // 1531 -> MEDIUM_UNAVAILABLE_UPGRADE_SKIP_USB_LOW_QUALITY_MEDIUMS + 129, // 1532 -> MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_DISRUPTIVE_FALSE + 135, // 1533 -> MEDIUM_UNAVAILABLE_SOFT_AP_DISRUPTIVE_FALSE + 119, // 1534 -> MEDIUM_UNAVAILABLE_ALREADY_HAVE_A_WIFI_DIRECT_GROUP + 120, // 1535 -> MEDIUM_UNAVAILABLE_ALREADY_HOSTING_HOTSPOT_FOR_OTHER_CLIENTS + 134, // 1536 -> MEDIUM_UNAVAILABLE_REJECT_L2CAP_ON_GATT_MULTIPLEX_CONNECTION + 137, // 1537 -> MEDIUM_UNAVAILABLE_UPGRADE_ON_SAME_MEDIUM + 148, // 1538 -> MEDIUM_UNAVAILABLE_WEB_RTC_NO_INTERNET + 39, // 2000 -> CLIENT_WIFI_DIRECT_ALREADY_HOSTING_DIRECT_GROUP_FOR_THIS_CLIENT + 40, // 2001 -> CLIENT_WIFI_HOTSPOT_ALREADY_HOSTING_HOTSPOT_FOR_THIS_CLIENT + 24, // 2002 -> CLIENT_DUPLICATE_ACCEPTING_BLE_CONNECTION_REQUEST + 26, // 2003 -> CLIENT_DUPLICATE_ACCEPTING_L2CAP_CONNECTION_REQUEST + 25, // 2004 -> CLIENT_DUPLICATE_ACCEPTING_BT_CONNECTION_REQUEST + 27, // 2005 -> CLIENT_DUPLICATE_ACCEPTING_LAN_CONNECTION_REQUEST + 28, // 2006 -> CLIENT_DUPLICATE_ACCEPTING_NFC_CONNECTION_REQUEST + 30, // 2007 -> CLIENT_DUPLICATE_ACCEPTING_WEB_RTC_CONNECTION_REQUEST + 31, // 2008 -> CLIENT_DUPLICATE_ACCEPTING_WIFI_AWARE_CONNECTION_REQUEST + 33, // 2009 -> CLIENT_DUPLICATE_ACCEPTING_WIFI_HOTSPOT_CONNECTION_REQUEST + 32, // 2010 -> CLIENT_DUPLICATE_ACCEPTING_WIFI_DIRECT_CONNECTION_REQUEST + 29, // 2011 -> CLIENT_DUPLICATE_ACCEPTING_USB_CONNECTION_REQUEST + 34, // 2012 -> CLIENT_DUPLICATE_WIFI_AWARE_CONNECTION_REQUEST + 36, // 2013 -> CLIENT_DUPLICATE_WIFI_DIRECT_CONNECTION_REQUEST + 37, // 2014 -> CLIENT_DUPLICATE_WIFI_HOTSPOT_CONNECTION_REQUEST + 35, // 2015 -> CLIENT_DUPLICATE_WIFI_AWARE_SUBSCRIBING_REQUEST + 38, // 2016 -> CLIENT_UNSUPPORTED_USB_TO_BE_UPGRADE_MEDIUM + 159, // 2500 -> MISCELLEANEOUS_BLUETOOTH_MAC_ADDRESS_NULL + 163, // 2501 -> MISCELLEANEOUS_MOVE_TO_NEW_MEDIUM + 168, // 2502 -> MISCELLEANEOUS_WIFI_HOTSPOT_SOFT_AP_BLOCKED_BY_PROVISION + 158, // 2503 -> MISCELLEANEOUS_BLE_SYSTEM_SERVICE_NULL + 162, // 2504 -> MISCELLEANEOUS_L2CAP_SYSTEM_SERVICE_NULL + 161, // 2505 -> MISCELLEANEOUS_BT_SYSTEM_SERVICE_NULL + 166, // 2506 -> MISCELLEANEOUS_WIFI_AWARE_SYSTEM_SERVICE_NULL + 167, // 2507 -> MISCELLEANEOUS_WIFI_DIRECT_SYSTEM_SERVICE_NULL + 170, // 2508 -> MISCELLEANEOUS_WIFI_LAN_SYSTEM_SERVICE_NULL + 169, // 2509 -> MISCELLEANEOUS_WIFI_HOTSPOT_SYSTEM_SERVICE_NULL + 160, // 2510 -> MISCELLEANEOUS_BT_NOT_ACCEPTING_CONNECTION_FOR_WORK_PROFILE + 164, // 2511 -> MISCELLEANEOUS_WEB_RTC_GET_DROIDGUARD_RESULT_FAILURE + 165, // 2512 -> MISCELLEANEOUS_WEB_RTC_TACHYON_SIGNALING_MESSENGER_NULL + 114, // 3000 -> IO_FILE_OPENING_ERROR + 115, // 3001 -> IO_FILE_READING_ERROR + 116, // 3002 -> IO_FILE_WRITING_ERROR + 117, // 3003 -> IO_FOLDER_CREATION_ERROR + 118, // 3004 -> IO_STREAM_CREATE_PIPE_FAILURE + 104, // 3005 -> IO_ENDPOINT_IO_ERROR_ON_BLE + 106, // 3006 -> IO_ENDPOINT_IO_ERROR_ON_L2CAP + 105, // 3007 -> IO_ENDPOINT_IO_ERROR_ON_BT + 110, // 3008 -> IO_ENDPOINT_IO_ERROR_ON_WEB_RTC + 107, // 3009 -> IO_ENDPOINT_IO_ERROR_ON_LAN + 112, // 3010 -> IO_ENDPOINT_IO_ERROR_ON_WIFI_DIRECT + 113, // 3011 -> IO_ENDPOINT_IO_ERROR_ON_WIFI_HOTSPOT + 111, // 3012 -> IO_ENDPOINT_IO_ERROR_ON_WIFI_AWARE + 108, // 3013 -> IO_ENDPOINT_IO_ERROR_ON_NFC + 109, // 3014 -> IO_ENDPOINT_IO_ERROR_ON_USB + 69, // 3500 -> CONNECTIVITY_WIFI_AWARE_ATTACH_FAILURE + 44, // 3501 -> CONNECTIVITY_BLUETOOTH_DEVICE_OBTAIN_FAILURE + 41, // 3502 -> CONNECTIVITY_BLE_CLIENT_SOCKET_CREATION_FAILURE + 54, // 3503 -> CONNECTIVITY_L2CAP_CLIENT_SOCKET_CREATION_FAILURE + 46, // 3504 -> CONNECTIVITY_BT_CLIENT_SOCKET_CREATION_FAILURE + 58, // 3505 -> CONNECTIVITY_LAN_CLIENT_SOCKET_CREATION_FAILURE + 62, // 3506 -> CONNECTIVITY_NFC_CLIENT_SOCKET_CREATION_FAILURE + 65, // 3507 -> CONNECTIVITY_WEB_RTC_CLIENT_SOCKET_CREATION_FAILURE + 70, // 3508 -> CONNECTIVITY_WIFI_AWARE_CLIENT_SOCKET_CREATION_FAILURE + 86, // 3509 -> CONNECTIVITY_WIFI_HOTSPOT_CLIENT_SOCKET_CREATION_FAILURE + 79, // 3510 -> CONNECTIVITY_WIFI_DIRECT_CLIENT_SOCKET_CREATION_FAILURE + 64, // 3511 -> CONNECTIVITY_USB_CLIENT_SOCKET_CREATION_FAILURE + 66, // 3512 -> CONNECTIVITY_WEB_RTC_CONNECT_TO_TACHYON_FAILURE + 42, // 3513 -> CONNECTIVITY_BLE_CREATE_GATT_CONNECTION_FAILURE + 73, // 3514 -> CONNECTIVITY_WIFI_AWARE_GET_REMOTE_IP_FRAME_FAILURE + 72, // 3515 -> CONNECTIVITY_WIFI_AWARE_GET_REMOTE_IP_ADDRESS_FAILURE + 81, // 3516 -> CONNECTIVITY_WIFI_DIRECT_INCONSISTENT_HOSTED_WIFI_BAND + 88, // 3517 -> CONNECTIVITY_WIFI_HOTSPOT_INCONSISTENT_HOSTED_WIFI_BAND + 45, // 3518 -> CONNECTIVITY_BLUETOOTH_INVALID_CREDENTIAL + 95, // 3519 -> CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL + 82, // 3520 -> CONNECTIVITY_WIFI_DIRECT_INVALID_CREDENTIAL + 89, // 3521 -> CONNECTIVITY_WIFI_HOTSPOT_INVALID_CREDENTIAL + 74, // 3522 -> CONNECTIVITY_WIFI_AWARE_INVALID_CREDENTIAL + 67, // 3523 -> CONNECTIVITY_WEB_RTC_INVALID_CREDENTIAL + 96, // 3524 -> CONNECTIVITY_WIFI_LAN_IP_ADDRESS_ERROR + 53, // 3525 -> CONNECTIVITY_L2CAP_CLIENT_OBTAIN_FAIURE + 55, // 3526 -> CONNECTIVITY_L2CAP_DATA_CONNECTION_FAILURE + 75, // 3527 -> CONNECTIVITY_WIFI_AWARE_L2MESSAGE_NETWORK_AVAILABLE_FRAME_NULL + 76, // 3528 -> CONNECTIVITY_WIFI_AWARE_L2MESSAGE_SEND_HOST_NETWORK_FRAME_FAILURE + 61, // 3529 -> CONNECTIVITY_LAN_UNREACHABLE + 90, // 3530 -> CONNECTIVITY_WIFI_HOTSPOT_LOHS_CREATION_FAILURE + 59, // 3531 -> CONNECTIVITY_LAN_GET_NETWORK_INTERFACES_FAILURE + 80, // 3532 -> CONNECTIVITY_WIFI_DIRECT_GET_NETWORK_INTERFACES_FAILURE + 87, // 3533 -> CONNECTIVITY_WIFI_HOTSPOT_GET_NETWORK_INTERFACES_FAILURE + 91, // 3534 -> CONNECTIVITY_WIFI_HOTSPOT_P2P_CHANNEL_INITIALIZE_FAILURE + 83, // 3535 -> CONNECTIVITY_WIFI_DIRECT_P2P_CHANNEL_INITIALIZE_FAILURE + 92, // 3536 -> CONNECTIVITY_WIFI_HOTSPOT_P2P_GROUP_CREATION_FAILURE + 84, // 3537 -> CONNECTIVITY_WIFI_DIRECT_P2P_GROUP_CREATION_FAILURE + 49, // 3538 -> CONNECTIVITY_GATT_SERVER_OPEN_FAILURE + 43, // 3539 -> CONNECTIVITY_BLE_SERVER_SOCKET_CREATION_FAILURE + 56, // 3540 -> CONNECTIVITY_L2CAP_SERVER_SOCKET_CREATION_FAILURE + 47, // 3541 -> CONNECTIVITY_BT_SERVER_SOCKET_CREATION_FAILURE + 60, // 3542 -> CONNECTIVITY_LAN_SERVER_SOCKET_CREATION_FAILURE + 68, // 3543 -> CONNECTIVITY_WEB_RTC_SERVER_SOCKET_CREATION_FAILURE + 77, // 3544 -> CONNECTIVITY_WIFI_AWARE_SERVER_SOCKET_CREATION_FAILURE + 93, // 3545 -> CONNECTIVITY_WIFI_HOTSPOT_SERVER_SOCKET_CREATION_FAILURE + 85, // 3546 -> CONNECTIVITY_WIFI_DIRECT_SERVER_SOCKET_CREATION_FAILURE + 63, // 3547 -> CONNECTIVITY_NFC_SERVER_SOCKET_CREATION_FAILURE + 94, // 3548 -> CONNECTIVITY_WIFI_HOTSPOT_SOFT_AP_CREATION_FAILURE + 78, // 3549 -> CONNECTIVITY_WIFI_AWARE_UPDATE_PUBLISH_FAILURE + 52, // 3550 -> CONNECTIVITY_GENERIC_WRITING_CHANNEL_IO_ERROR + 51, // 3551 -> CONNECTIVITY_GENERIC_WRITE_CLIENT_INTRODUCTION_ACK_IO_ERROR + 71, // 3552 -> CONNECTIVITY_WIFI_AWARE_DISCOVERED_PEER_NULL + 50, // 3553 -> CONNECTIVITY_GENERIC_PAYLOAD_SENT_ERROR + 57, // 3554 -> CONNECTIVITY_L2CAP_SERVER_SOCKET_CREATION_SECURITY_EXCEPTION_FAILURE + 48, // 3555 -> CONNECTIVITY_BT_SERVER_SOCKET_CREATION_SECURITY_EXCEPTION_FAILURE + 171, // 4500 -> NEARBY_BLE_ADVERTISEMENT_MAPPING_TO_MAC_ERROR + 176, // 4501 -> NEARBY_BLUETOOTH_MAC_ADDRESS_INVALID_FOR_CONNECT + 211, // 4502 -> NEARBY_WEB_RTC_CONNECTION_FLOW_NULL + 182, // 4503 -> NEARBY_GENERIC_CONNECTION_CLOSED + 172, // 4504 -> NEARBY_BLE_ENDPOINT_CHANNEL_CREATION_FAILURE + 198, // 4505 -> NEARBY_L2CAP_ENDPOINT_CHANNEL_CREATION_FAILURE + 177, // 4506 -> NEARBY_BT_ENDPOINT_CHANNEL_CREATION_FAILURE + 202, // 4507 -> NEARBY_LAN_ENDPOINT_CHANNEL_CREATION_FAILURE + 207, // 4508 -> NEARBY_NFC_ENDPOINT_CHANNEL_CREATION_FAILURE + 215, // 4509 -> NEARBY_WIFI_AWARE_ENDPOINT_CHANNEL_CREATION_FAILURE + 229, // 4510 -> NEARBY_WIFI_HOTSPOT_ENDPOINT_CHANNEL_CREATION_FAILURE + 218, // 4511 -> NEARBY_WIFI_DIRECT_ENDPOINT_CHANNEL_CREATION_FAILURE + 212, // 4512 -> NEARBY_WEB_RTC_ENDPOINT_CHANNEL_CREATION_FAILURE + 209, // 4513 -> NEARBY_USB_ENDPOINT_CHANNEL_CREATION_FAILURE + 183, // 4514 -> NEARBY_GENERIC_ENDPOINT_UNENCRYPTED + 173, // 4515 -> NEARBY_BLE_GATT_ADVERTISEMENT_NULL_FOR_CONNECTION + 219, // 4516 -> NEARBY_WIFI_DIRECT_HOST_ON_SRD_CHANNELS + 230, // 4517 -> NEARBY_WIFI_HOTSPOT_HOST_ON_SRD_CHANNELS + 174, // 4518 -> NEARBY_BLE_GATT_NULL_CALLBACK + 199, // 4519 -> NEARBY_L2CAP_NULL_CALLBACK + 179, // 4520 -> NEARBY_BT_NULL_CALLBACK + 210, // 4521 -> NEARBY_USB_NULL_CALLBACK + 208, // 4522 -> NEARBY_NFC_NULL_CALLBACK + 216, // 4523 -> NEARBY_WIFI_AWARE_NULL_CALLBACK + 213, // 4524 -> NEARBY_WEB_RTC_NULL_CALLBACK + 204, // 4525 -> NEARBY_LAN_NULL_CALLBACK + 233, // 4526 -> NEARBY_WIFI_HOTSPOT_NULL_CALLBACK + 221, // 4527 -> NEARBY_WIFI_DIRECT_NULL_CALLBACK + 223, // 4528 -> NEARBY_WIFI_DIRECT_NULL_SSID + 222, // 4529 -> NEARBY_WIFI_DIRECT_NULL_PASSWORD + 178, // 4530 -> NEARBY_BT_MULTIPLEX_SOCKET_DISABLED + 203, // 4531 -> NEARBY_LAN_MULTIPLEX_SOCKET_DISABLED + 185, // 4532 -> NEARBY_GENERIC_NEW_ENDPOINT_CHANNEL_NULL + 220, // 4533 -> NEARBY_WIFI_DIRECT_NO_GROUP_FOR_LISTENING + 232, // 4534 -> NEARBY_WIFI_HOTSPOT_NO_HOTSPOT_FOR_LISTENING + 186, // 4535 -> NEARBY_GENERIC_OLD_ENDPOINT_CHANNEL_NULL + 175, // 4536 -> NEARBY_BLE_OPERATION_REGISTERED_FAILED + 200, // 4537 -> NEARBY_L2CAP_OPERATION_REGISTERED_FAILED + 180, // 4538 -> NEARBY_BT_OPERATION_REGISTERED_FAILED + 205, // 4539 -> NEARBY_LAN_OPERATION_REGISTERED_FAILED + 214, // 4540 -> NEARBY_WEB_RTC_OPERATION_REGISTERED_FAILED + 217, // 4541 -> NEARBY_WIFI_AWARE_OPERATION_REGISTERED_FAILED + 228, // 4542 -> NEARBY_WIFI_HOTSPOT_DIRECT_OPERATION_REGISTERED_FAILED + 236, // 4543 -> NEARBY_WIFI_HOTSPOT_SOFT_AP_OPERATION_REGISTERED_FAILED + 231, // 4544 -> NEARBY_WIFI_HOTSPOT_LOHS_OPERATION_REGISTERED_FAILED + 227, // 4545 -> NEARBY_WIFI_HOTSPOT_CLIENT_OPERATION_REGISTERED_FAILED + 224, // 4546 -> NEARBY_WIFI_DIRECT_OPERATION_REGISTERED_FAILED + 187, // 4547 -> NEARBY_GENERIC_OUTGOING_PAYLOAD_CREATION_FAILURE + 234, // 4548 -> NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_2G_BUT_AP_5G + 225, // 4549 -> NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_2G_BUT_AP_5G + 235, // 4550 -> NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_5G_BUT_AP_2G + 226, // 4551 -> NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_5G_BUT_AP_2G + 184, // 4552 -> NEARBY_GENERIC_INCOMING_PAYLOAD_NOT_DATA_TYPE + 191, // 4553 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_EVENT_TYPE_ERROR + 193, // 4554 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FRAME_TYPE_ERROR + 192, // 4555 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FORMAT_ERROR + 188, // 4556 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_EVENT_TYPE_ERROR + 190, // 4557 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FRAME_TYPE_ERROR + 189, // 4558 -> NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FORMAT_ERROR + 194, // 4559 -> NEARBY_GENERIC_REMOTE_ENDPOINT_STATUS_ERROR + 195, // 4560 -> NEARBY_GENERIC_REMOTE_REPORT_PAYLOADS_ERROR + 196, // 4561 -> NEARBY_GENERIC_REMOTE_UPGRADE_FAILURE + 197, // 4562 -> NEARBY_GENERIC_SEND_PAYLOAD_EXECUTOR_NULL + 181, // 4563 -> NEARBY_BT_VIRTUAL_SOCKET_CREATION_FAILURE + 206, // 4564 -> NEARBY_LAN_VIRTUAL_SOCKET_CREATION_FAILURE + 237, // 4565 -> NEARBY_WIFI_LAN_IP_ADDRESS_ERROR + 201, // 4566 -> NEARBY_L2CAP_PSM_NOT_POSITIVE +}; + +const std::string& OperationResultDetail_Name( + OperationResultDetail value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + OperationResultDetail_entries, + OperationResultDetail_entries_by_number, + 238, OperationResultDetail_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + OperationResultDetail_entries, + OperationResultDetail_entries_by_number, + 238, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + OperationResultDetail_strings[idx].get(); +} +bool OperationResultDetail_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, OperationResultDetail* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + OperationResultDetail_entries, 238, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} // @@protoc_insertion_point(namespace_scope) } // namespace connections diff --git a/compiled_proto/proto/connections_enums.pb.h b/compiled_proto/proto/connections_enums.pb.h index 079b18b6..5f00902d 100644 --- a/compiled_proto/proto/connections_enums.pb.h +++ b/compiled_proto/proto/connections_enums.pb.h @@ -517,6 +517,288 @@ inline const std::string& PowerLevel_Name(T enum_t_value) { } bool PowerLevel_Parse( ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, PowerLevel* value); +enum OperationResultCategory : int { + CATEGORY_UNKNOWN = 0, + CATEGORY_SUCCESS = 1, + CATEGORY_CLIENT_CANCELLATION = 2, + CATEGORY_MEDIUM_UNAVAILABLE = 3, + CATEGORY_DEVICE_STATE_ERROR = 4, + CATEGORY_CLIENT_ERROR = 5, + CATEGORY_NEARBY_ERROR = 6, + CATEGORY_CONNECTIVITY_ERROR = 7, + CATEGORY_MISCELLANEOUS = 8, + CATEGORY_IO_ERROR = 9 +}; +bool OperationResultCategory_IsValid(int value); +constexpr OperationResultCategory OperationResultCategory_MIN = CATEGORY_UNKNOWN; +constexpr OperationResultCategory OperationResultCategory_MAX = CATEGORY_IO_ERROR; +constexpr int OperationResultCategory_ARRAYSIZE = OperationResultCategory_MAX + 1; + +const std::string& OperationResultCategory_Name(OperationResultCategory value); +template +inline const std::string& OperationResultCategory_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function OperationResultCategory_Name."); + return OperationResultCategory_Name(static_cast(enum_t_value)); +} +bool OperationResultCategory_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, OperationResultCategory* value); +enum OperationResultDetail : int { + DETAIL_UNKNOWN = 0, + DETAIL_SUCCESS = 1, + CLIENT_CANCELLATION_REMOTE_IN_CANCELED_STATE = 500, + CLIENT_CANCELLATION_LOCAL_CANCEL_PAYLOAD = 501, + CLIENT_CANCELLATION_REMOTE_CANCEL_PAYLOAD = 502, + CLIENT_CANCELLATION_UPGRADE_CANCELED_BY_REMOTE = 503, + CLIENT_CANCELLATION_CANCEL_BLE_OUTGOING_CONNECTION = 504, + CLIENT_CANCELLATION_CANCEL_BT_OUTGOING_CONNECTION = 505, + CLIENT_CANCELLATION_CANCEL_L2CAP_OUTGOING_CONNECTION = 506, + CLIENT_CANCELLATION_CANCEL_LAN_OUTGOING_CONNECTION = 507, + CLIENT_CANCELLATION_CANCEL_NFC_OUTGOING_CONNECTION = 508, + CLIENT_CANCELLATION_CANCEL_USB_OUTGOING_CONNECTION = 509, + CLIENT_CANCELLATION_CANCEL_WIFI_AWARE_OUTGOING_CONNECTION = 510, + CLIENT_CANCELLATION_CANCEL_WIFI_DIRECT_OUTGOING_CONNECTION = 511, + CLIENT_CANCELLATION_CANCEL_WIFI_HOTSPOT_OUTGOING_CONNECTION = 512, + CLIENT_CANCELLATION_CANCEL_WEB_RTC_OUTGOING_CONNECTION = 513, + CLIENT_CANCELLATION_CANCEL_OUTGOING_CONNECTION = 514, + CLIENT_CANCELLATION_CANCEL_INCOMING_CONNECTION = 515, + CLIENT_CANCELLATION_WIFI_AWARE_SERVER_SOCKET_CREATION = 516, + CLIENT_CANCELLATION_WIFI_DIRECT_SERVER_SOCKET_CREATION = 517, + CLIENT_CANCELLATION_WIFI_HOTSPOT_SERVER_SOCKET_CREATION = 518, + CLIENT_CANCELLATION_BT_SERVER_SOCKET_CREATION = 519, + CLIENT_CANCELLATION_WEB_RTC_SERVER_SOCKET_CREATION = 520, + CLIENT_CANCELLATION_WIFI_LAN_SERVER_SOCKET_CREATION = 521, + CLIENT_CANCELLATION_LOCAL_DISCONNECT = 522, + CLIENT_CANCELLATION_REMOTE_DISCONNECT = 523, + DEVICE_STATE_ERROR_UNFINISHED_UPGRADE_ATTEMPTS = 1000, + DEVICE_STATE_ERROR_USER_HOTSPOT_ENABLED = 1001, + DEVICE_STATE_LOCATION_DISABLED = 1002, + DEVICE_STATE_RADIO_DISABLING_FAILURE = 1003, + DEVICE_STATE_RADIO_ENABLING_FAILURE = 1004, + MEDIUM_UNAVAILABLE_WIFI_AWARE_RESOURCE_NOT_AVAILABLE = 1500, + MEDIUM_UNAVAILABLE_DIRECT_HOTSPOT_NOT_SUPPORT = 1501, + MEDIUM_UNAVAILABLE_SOFT_AP_NOT_SUPPORT = 1502, + MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT = 1503, + MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_NOT_SUPPORT_5G = 1504, + MEDIUM_UNAVAILABLE_BLE_NOT_AVAILABLE = 1505, + MEDIUM_UNAVAILABLE_L2CAP_NOT_AVAILABLE = 1506, + MEDIUM_UNAVAILABLE_BLUETOOTH_NOT_AVAILABLE = 1507, + MEDIUM_UNAVAILABLE_WEB_RTC_NOT_AVAILABLE = 1508, + MEDIUM_UNAVAILABLE_WIFI_AWARE_NOT_AVAILABLE = 1509, + MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NOT_AVAILABLE = 1510, + MEDIUM_UNAVAILABLE_WIFI_DIRECT_NOT_AVAILABLE = 1511, + MEDIUM_UNAVAILABLE_NFC_NOT_AVAILABLE = 1512, + MEDIUM_UNAVAILABLE_LAN_NOT_AVAILABLE = 1513, + MEDIUM_UNAVAILABLE_USB_NOT_AVAILABLE = 1514, + MEDIUM_UNAVAILABLE_BLE_NC_LOGICAL_NOT_AVAILABLE = 1515, + MEDIUM_UNAVAILABLE_BT_NC_LOGICAL_NOT_AVAILABLE = 1516, + MEDIUM_UNAVAILABLE_LAN_NC_LOGICAL_NOT_AVAILABLE = 1517, + MEDIUM_UNAVAILABLE_NFC_NC_LOGICAL_NOT_AVAILABLE = 1518, + MEDIUM_UNAVAILABLE_USB_NC_LOGICAL_NOT_AVAILABLE = 1519, + MEDIUM_UNAVAILABLE_WEB_RTC_NC_LOGICAL_NOT_AVAILABLE = 1520, + MEDIUM_UNAVAILABLE_WIFI_AWARE_NC_LOGICAL_NOT_AVAILABLE = 1521, + MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_NC_LOGICAL_NOT_AVAILABLE = 1522, + MEDIUM_UNAVAILABLE_WIFI_DIRECT_NC_LOGICAL_NOT_AVAILABLE = 1523, + MEDIUM_UNAVAILABLE_WIFI_HOTSPOT_P2P_RESOURCE_NOT_AVAILABLE = 1524, + MEDIUM_UNAVAILABLE_WIFI_DIRECT_P2P_RESOURCE_NOT_AVAILABLE = 1525, + MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BLE_LOW_QUALITY_MEDIUMS = 1526, + MEDIUM_UNAVAILABLE_UPGRADE_SKIP_L2CAP_LOW_QUALITY_MEDIUMS = 1527, + MEDIUM_UNAVAILABLE_UPGRADE_SKIP_WEB_RTC_LOW_QUALITY_MEDIUMS = 1528, + MEDIUM_UNAVAILABLE_UPGRADE_SKIP_LAN_LOW_QUALITY_MEDIUMS = 1529, + MEDIUM_UNAVAILABLE_UPGRADE_SKIP_BT_LOW_QUALITY_MEDIUMS = 1530, + MEDIUM_UNAVAILABLE_UPGRADE_SKIP_USB_LOW_QUALITY_MEDIUMS = 1531, + MEDIUM_UNAVAILABLE_LOCAL_ONLY_HOTSPOT_DISRUPTIVE_FALSE = 1532, + MEDIUM_UNAVAILABLE_SOFT_AP_DISRUPTIVE_FALSE = 1533, + MEDIUM_UNAVAILABLE_ALREADY_HAVE_A_WIFI_DIRECT_GROUP = 1534, + MEDIUM_UNAVAILABLE_ALREADY_HOSTING_HOTSPOT_FOR_OTHER_CLIENTS = 1535, + MEDIUM_UNAVAILABLE_REJECT_L2CAP_ON_GATT_MULTIPLEX_CONNECTION = 1536, + MEDIUM_UNAVAILABLE_UPGRADE_ON_SAME_MEDIUM = 1537, + MEDIUM_UNAVAILABLE_WEB_RTC_NO_INTERNET = 1538, + CLIENT_WIFI_DIRECT_ALREADY_HOSTING_DIRECT_GROUP_FOR_THIS_CLIENT = 2000, + CLIENT_WIFI_HOTSPOT_ALREADY_HOSTING_HOTSPOT_FOR_THIS_CLIENT = 2001, + CLIENT_DUPLICATE_ACCEPTING_BLE_CONNECTION_REQUEST = 2002, + CLIENT_DUPLICATE_ACCEPTING_L2CAP_CONNECTION_REQUEST = 2003, + CLIENT_DUPLICATE_ACCEPTING_BT_CONNECTION_REQUEST = 2004, + CLIENT_DUPLICATE_ACCEPTING_LAN_CONNECTION_REQUEST = 2005, + CLIENT_DUPLICATE_ACCEPTING_NFC_CONNECTION_REQUEST = 2006, + CLIENT_DUPLICATE_ACCEPTING_WEB_RTC_CONNECTION_REQUEST = 2007, + CLIENT_DUPLICATE_ACCEPTING_WIFI_AWARE_CONNECTION_REQUEST = 2008, + CLIENT_DUPLICATE_ACCEPTING_WIFI_HOTSPOT_CONNECTION_REQUEST = 2009, + CLIENT_DUPLICATE_ACCEPTING_WIFI_DIRECT_CONNECTION_REQUEST = 2010, + CLIENT_DUPLICATE_ACCEPTING_USB_CONNECTION_REQUEST = 2011, + CLIENT_DUPLICATE_WIFI_AWARE_CONNECTION_REQUEST = 2012, + CLIENT_DUPLICATE_WIFI_DIRECT_CONNECTION_REQUEST = 2013, + CLIENT_DUPLICATE_WIFI_HOTSPOT_CONNECTION_REQUEST = 2014, + CLIENT_DUPLICATE_WIFI_AWARE_SUBSCRIBING_REQUEST = 2015, + CLIENT_UNSUPPORTED_USB_TO_BE_UPGRADE_MEDIUM = 2016, + MISCELLEANEOUS_BLUETOOTH_MAC_ADDRESS_NULL = 2500, + MISCELLEANEOUS_MOVE_TO_NEW_MEDIUM = 2501, + MISCELLEANEOUS_WIFI_HOTSPOT_SOFT_AP_BLOCKED_BY_PROVISION = 2502, + MISCELLEANEOUS_BLE_SYSTEM_SERVICE_NULL = 2503, + MISCELLEANEOUS_L2CAP_SYSTEM_SERVICE_NULL = 2504, + MISCELLEANEOUS_BT_SYSTEM_SERVICE_NULL = 2505, + MISCELLEANEOUS_WIFI_AWARE_SYSTEM_SERVICE_NULL = 2506, + MISCELLEANEOUS_WIFI_DIRECT_SYSTEM_SERVICE_NULL = 2507, + MISCELLEANEOUS_WIFI_LAN_SYSTEM_SERVICE_NULL = 2508, + MISCELLEANEOUS_WIFI_HOTSPOT_SYSTEM_SERVICE_NULL = 2509, + MISCELLEANEOUS_BT_NOT_ACCEPTING_CONNECTION_FOR_WORK_PROFILE = 2510, + MISCELLEANEOUS_WEB_RTC_GET_DROIDGUARD_RESULT_FAILURE = 2511, + MISCELLEANEOUS_WEB_RTC_TACHYON_SIGNALING_MESSENGER_NULL = 2512, + IO_FILE_OPENING_ERROR = 3000, + IO_FILE_READING_ERROR = 3001, + IO_FILE_WRITING_ERROR = 3002, + IO_FOLDER_CREATION_ERROR = 3003, + IO_STREAM_CREATE_PIPE_FAILURE = 3004, + IO_ENDPOINT_IO_ERROR_ON_BLE = 3005, + IO_ENDPOINT_IO_ERROR_ON_L2CAP = 3006, + IO_ENDPOINT_IO_ERROR_ON_BT = 3007, + IO_ENDPOINT_IO_ERROR_ON_WEB_RTC = 3008, + IO_ENDPOINT_IO_ERROR_ON_LAN = 3009, + IO_ENDPOINT_IO_ERROR_ON_WIFI_DIRECT = 3010, + IO_ENDPOINT_IO_ERROR_ON_WIFI_HOTSPOT = 3011, + IO_ENDPOINT_IO_ERROR_ON_WIFI_AWARE = 3012, + IO_ENDPOINT_IO_ERROR_ON_NFC = 3013, + IO_ENDPOINT_IO_ERROR_ON_USB = 3014, + CONNECTIVITY_WIFI_AWARE_ATTACH_FAILURE = 3500, + CONNECTIVITY_BLUETOOTH_DEVICE_OBTAIN_FAILURE = 3501, + CONNECTIVITY_BLE_CLIENT_SOCKET_CREATION_FAILURE = 3502, + CONNECTIVITY_L2CAP_CLIENT_SOCKET_CREATION_FAILURE = 3503, + CONNECTIVITY_BT_CLIENT_SOCKET_CREATION_FAILURE = 3504, + CONNECTIVITY_LAN_CLIENT_SOCKET_CREATION_FAILURE = 3505, + CONNECTIVITY_NFC_CLIENT_SOCKET_CREATION_FAILURE = 3506, + CONNECTIVITY_WEB_RTC_CLIENT_SOCKET_CREATION_FAILURE = 3507, + CONNECTIVITY_WIFI_AWARE_CLIENT_SOCKET_CREATION_FAILURE = 3508, + CONNECTIVITY_WIFI_HOTSPOT_CLIENT_SOCKET_CREATION_FAILURE = 3509, + CONNECTIVITY_WIFI_DIRECT_CLIENT_SOCKET_CREATION_FAILURE = 3510, + CONNECTIVITY_USB_CLIENT_SOCKET_CREATION_FAILURE = 3511, + CONNECTIVITY_WEB_RTC_CONNECT_TO_TACHYON_FAILURE = 3512, + CONNECTIVITY_BLE_CREATE_GATT_CONNECTION_FAILURE = 3513, + CONNECTIVITY_WIFI_AWARE_GET_REMOTE_IP_FRAME_FAILURE = 3514, + CONNECTIVITY_WIFI_AWARE_GET_REMOTE_IP_ADDRESS_FAILURE = 3515, + CONNECTIVITY_WIFI_DIRECT_INCONSISTENT_HOSTED_WIFI_BAND = 3516, + CONNECTIVITY_WIFI_HOTSPOT_INCONSISTENT_HOSTED_WIFI_BAND = 3517, + CONNECTIVITY_BLUETOOTH_INVALID_CREDENTIAL = 3518, + CONNECTIVITY_WIFI_LAN_INVALID_CREDENTIAL = 3519, + CONNECTIVITY_WIFI_DIRECT_INVALID_CREDENTIAL = 3520, + CONNECTIVITY_WIFI_HOTSPOT_INVALID_CREDENTIAL = 3521, + CONNECTIVITY_WIFI_AWARE_INVALID_CREDENTIAL = 3522, + CONNECTIVITY_WEB_RTC_INVALID_CREDENTIAL = 3523, + CONNECTIVITY_WIFI_LAN_IP_ADDRESS_ERROR = 3524, + CONNECTIVITY_L2CAP_CLIENT_OBTAIN_FAIURE = 3525, + CONNECTIVITY_L2CAP_DATA_CONNECTION_FAILURE = 3526, + CONNECTIVITY_WIFI_AWARE_L2MESSAGE_NETWORK_AVAILABLE_FRAME_NULL = 3527, + CONNECTIVITY_WIFI_AWARE_L2MESSAGE_SEND_HOST_NETWORK_FRAME_FAILURE = 3528, + CONNECTIVITY_LAN_UNREACHABLE = 3529, + CONNECTIVITY_WIFI_HOTSPOT_LOHS_CREATION_FAILURE = 3530, + CONNECTIVITY_LAN_GET_NETWORK_INTERFACES_FAILURE = 3531, + CONNECTIVITY_WIFI_DIRECT_GET_NETWORK_INTERFACES_FAILURE = 3532, + CONNECTIVITY_WIFI_HOTSPOT_GET_NETWORK_INTERFACES_FAILURE = 3533, + CONNECTIVITY_WIFI_HOTSPOT_P2P_CHANNEL_INITIALIZE_FAILURE = 3534, + CONNECTIVITY_WIFI_DIRECT_P2P_CHANNEL_INITIALIZE_FAILURE = 3535, + CONNECTIVITY_WIFI_HOTSPOT_P2P_GROUP_CREATION_FAILURE = 3536, + CONNECTIVITY_WIFI_DIRECT_P2P_GROUP_CREATION_FAILURE = 3537, + CONNECTIVITY_GATT_SERVER_OPEN_FAILURE = 3538, + CONNECTIVITY_BLE_SERVER_SOCKET_CREATION_FAILURE = 3539, + CONNECTIVITY_L2CAP_SERVER_SOCKET_CREATION_FAILURE = 3540, + CONNECTIVITY_BT_SERVER_SOCKET_CREATION_FAILURE = 3541, + CONNECTIVITY_LAN_SERVER_SOCKET_CREATION_FAILURE = 3542, + CONNECTIVITY_WEB_RTC_SERVER_SOCKET_CREATION_FAILURE = 3543, + CONNECTIVITY_WIFI_AWARE_SERVER_SOCKET_CREATION_FAILURE = 3544, + CONNECTIVITY_WIFI_HOTSPOT_SERVER_SOCKET_CREATION_FAILURE = 3545, + CONNECTIVITY_WIFI_DIRECT_SERVER_SOCKET_CREATION_FAILURE = 3546, + CONNECTIVITY_NFC_SERVER_SOCKET_CREATION_FAILURE = 3547, + CONNECTIVITY_WIFI_HOTSPOT_SOFT_AP_CREATION_FAILURE = 3548, + CONNECTIVITY_WIFI_AWARE_UPDATE_PUBLISH_FAILURE = 3549, + CONNECTIVITY_GENERIC_WRITING_CHANNEL_IO_ERROR = 3550, + CONNECTIVITY_GENERIC_WRITE_CLIENT_INTRODUCTION_ACK_IO_ERROR = 3551, + CONNECTIVITY_WIFI_AWARE_DISCOVERED_PEER_NULL = 3552, + CONNECTIVITY_GENERIC_PAYLOAD_SENT_ERROR = 3553, + CONNECTIVITY_L2CAP_SERVER_SOCKET_CREATION_SECURITY_EXCEPTION_FAILURE = 3554, + CONNECTIVITY_BT_SERVER_SOCKET_CREATION_SECURITY_EXCEPTION_FAILURE = 3555, + NEARBY_BLE_ADVERTISEMENT_MAPPING_TO_MAC_ERROR = 4500, + NEARBY_BLUETOOTH_MAC_ADDRESS_INVALID_FOR_CONNECT = 4501, + NEARBY_WEB_RTC_CONNECTION_FLOW_NULL = 4502, + NEARBY_GENERIC_CONNECTION_CLOSED = 4503, + NEARBY_BLE_ENDPOINT_CHANNEL_CREATION_FAILURE = 4504, + NEARBY_L2CAP_ENDPOINT_CHANNEL_CREATION_FAILURE = 4505, + NEARBY_BT_ENDPOINT_CHANNEL_CREATION_FAILURE = 4506, + NEARBY_LAN_ENDPOINT_CHANNEL_CREATION_FAILURE = 4507, + NEARBY_NFC_ENDPOINT_CHANNEL_CREATION_FAILURE = 4508, + NEARBY_WIFI_AWARE_ENDPOINT_CHANNEL_CREATION_FAILURE = 4509, + NEARBY_WIFI_HOTSPOT_ENDPOINT_CHANNEL_CREATION_FAILURE = 4510, + NEARBY_WIFI_DIRECT_ENDPOINT_CHANNEL_CREATION_FAILURE = 4511, + NEARBY_WEB_RTC_ENDPOINT_CHANNEL_CREATION_FAILURE = 4512, + NEARBY_USB_ENDPOINT_CHANNEL_CREATION_FAILURE = 4513, + NEARBY_GENERIC_ENDPOINT_UNENCRYPTED = 4514, + NEARBY_BLE_GATT_ADVERTISEMENT_NULL_FOR_CONNECTION = 4515, + NEARBY_WIFI_DIRECT_HOST_ON_SRD_CHANNELS = 4516, + NEARBY_WIFI_HOTSPOT_HOST_ON_SRD_CHANNELS = 4517, + NEARBY_BLE_GATT_NULL_CALLBACK = 4518, + NEARBY_L2CAP_NULL_CALLBACK = 4519, + NEARBY_BT_NULL_CALLBACK = 4520, + NEARBY_USB_NULL_CALLBACK = 4521, + NEARBY_NFC_NULL_CALLBACK = 4522, + NEARBY_WIFI_AWARE_NULL_CALLBACK = 4523, + NEARBY_WEB_RTC_NULL_CALLBACK = 4524, + NEARBY_LAN_NULL_CALLBACK = 4525, + NEARBY_WIFI_HOTSPOT_NULL_CALLBACK = 4526, + NEARBY_WIFI_DIRECT_NULL_CALLBACK = 4527, + NEARBY_WIFI_DIRECT_NULL_SSID = 4528, + NEARBY_WIFI_DIRECT_NULL_PASSWORD = 4529, + NEARBY_BT_MULTIPLEX_SOCKET_DISABLED = 4530, + NEARBY_LAN_MULTIPLEX_SOCKET_DISABLED = 4531, + NEARBY_GENERIC_NEW_ENDPOINT_CHANNEL_NULL = 4532, + NEARBY_WIFI_DIRECT_NO_GROUP_FOR_LISTENING = 4533, + NEARBY_WIFI_HOTSPOT_NO_HOTSPOT_FOR_LISTENING = 4534, + NEARBY_GENERIC_OLD_ENDPOINT_CHANNEL_NULL = 4535, + NEARBY_BLE_OPERATION_REGISTERED_FAILED = 4536, + NEARBY_L2CAP_OPERATION_REGISTERED_FAILED = 4537, + NEARBY_BT_OPERATION_REGISTERED_FAILED = 4538, + NEARBY_LAN_OPERATION_REGISTERED_FAILED = 4539, + NEARBY_WEB_RTC_OPERATION_REGISTERED_FAILED = 4540, + NEARBY_WIFI_AWARE_OPERATION_REGISTERED_FAILED = 4541, + NEARBY_WIFI_HOTSPOT_DIRECT_OPERATION_REGISTERED_FAILED = 4542, + NEARBY_WIFI_HOTSPOT_SOFT_AP_OPERATION_REGISTERED_FAILED = 4543, + NEARBY_WIFI_HOTSPOT_LOHS_OPERATION_REGISTERED_FAILED = 4544, + NEARBY_WIFI_HOTSPOT_CLIENT_OPERATION_REGISTERED_FAILED = 4545, + NEARBY_WIFI_DIRECT_OPERATION_REGISTERED_FAILED = 4546, + NEARBY_GENERIC_OUTGOING_PAYLOAD_CREATION_FAILURE = 4547, + NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_2G_BUT_AP_5G = 4548, + NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_2G_BUT_AP_5G = 4549, + NEARBY_WIFI_HOTSPOT_P2P_NON_DBS_WANT_5G_BUT_AP_2G = 4550, + NEARBY_WIFI_DIRECT_P2P_NON_DBS_WANT_5G_BUT_AP_2G = 4551, + NEARBY_GENERIC_INCOMING_PAYLOAD_NOT_DATA_TYPE = 4552, + NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_EVENT_TYPE_ERROR = 4553, + NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FRAME_TYPE_ERROR = 4554, + NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_FORMAT_ERROR = 4555, + NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_EVENT_TYPE_ERROR = 4556, + NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FRAME_TYPE_ERROR = 4557, + NEARBY_GENERIC_READ_CLIENT_INTRODUCTION_ACK_FORMAT_ERROR = 4558, + NEARBY_GENERIC_REMOTE_ENDPOINT_STATUS_ERROR = 4559, + NEARBY_GENERIC_REMOTE_REPORT_PAYLOADS_ERROR = 4560, + NEARBY_GENERIC_REMOTE_UPGRADE_FAILURE = 4561, + NEARBY_GENERIC_SEND_PAYLOAD_EXECUTOR_NULL = 4562, + NEARBY_BT_VIRTUAL_SOCKET_CREATION_FAILURE = 4563, + NEARBY_LAN_VIRTUAL_SOCKET_CREATION_FAILURE = 4564, + NEARBY_WIFI_LAN_IP_ADDRESS_ERROR = 4565, + NEARBY_L2CAP_PSM_NOT_POSITIVE = 4566 +}; +bool OperationResultDetail_IsValid(int value); +constexpr OperationResultDetail OperationResultDetail_MIN = DETAIL_UNKNOWN; +constexpr OperationResultDetail OperationResultDetail_MAX = NEARBY_L2CAP_PSM_NOT_POSITIVE; +constexpr int OperationResultDetail_ARRAYSIZE = OperationResultDetail_MAX + 1; + +const std::string& OperationResultDetail_Name(OperationResultDetail value); +template +inline const std::string& OperationResultDetail_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function OperationResultDetail_Name."); + return OperationResultDetail_Name(static_cast(enum_t_value)); +} +bool OperationResultDetail_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, OperationResultDetail* value); // =================================================================== @@ -560,6 +842,8 @@ template <> struct is_proto_enum< ::location::nearby::proto::connections::Bandwi template <> struct is_proto_enum< ::location::nearby::proto::connections::BandwidthUpgradeErrorStage> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::connections::LogSource> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::connections::PowerLevel> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::connections::OperationResultCategory> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::connections::OperationResultDetail> : ::std::true_type {}; PROTOBUF_NAMESPACE_CLOSE diff --git a/compiled_proto/proto/mediums/ble_frames.pb.cc b/compiled_proto/proto/mediums/ble_frames.pb.cc index 42f55f75..e52eb70c 100644 --- a/compiled_proto/proto/mediums/ble_frames.pb.cc +++ b/compiled_proto/proto/mediums/ble_frames.pb.cc @@ -35,6 +35,7 @@ PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT SocketControlFrameDefaultTypeIn constexpr IntroductionFrame::IntroductionFrame( ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) : service_id_hash_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , service_id_hash_salt_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) , socket_version_(0) {} struct IntroductionFrameDefaultTypeInternal { @@ -552,6 +553,9 @@ class IntroductionFrame::_Internal { (*has_bits)[0] |= 1u; } static void set_has_socket_version(HasBits* has_bits) { + (*has_bits)[0] |= 4u; + } + static void set_has_service_id_hash_salt(HasBits* has_bits) { (*has_bits)[0] |= 2u; } }; @@ -577,6 +581,14 @@ IntroductionFrame::IntroductionFrame(const IntroductionFrame& from) service_id_hash_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_service_id_hash(), GetArenaForAllocation()); } + service_id_hash_salt_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + service_id_hash_salt_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_service_id_hash_salt()) { + service_id_hash_salt_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_service_id_hash_salt(), + GetArenaForAllocation()); + } socket_version_ = from.socket_version_; // @@protoc_insertion_point(copy_constructor:location.nearby.mediums.IntroductionFrame) } @@ -586,6 +598,10 @@ service_id_hash_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptySt #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING service_id_hash_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +service_id_hash_salt_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + service_id_hash_salt_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING socket_version_ = 0; } @@ -599,6 +615,7 @@ IntroductionFrame::~IntroductionFrame() { inline void IntroductionFrame::SharedDtor() { GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); service_id_hash_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + service_id_hash_salt_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); } void IntroductionFrame::ArenaDtor(void* object) { @@ -618,8 +635,13 @@ void IntroductionFrame::Clear() { (void) cached_has_bits; cached_has_bits = _has_bits_[0]; - if (cached_has_bits & 0x00000001u) { - service_id_hash_.ClearNonDefaultToEmpty(); + if (cached_has_bits & 0x00000003u) { + if (cached_has_bits & 0x00000001u) { + service_id_hash_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000002u) { + service_id_hash_salt_.ClearNonDefaultToEmpty(); + } } socket_version_ = 0; _has_bits_.Clear(); @@ -655,6 +677,15 @@ const char* IntroductionFrame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESP } else goto handle_unusual; continue; + // optional string service_id_hash_salt = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_service_id_hash_salt(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; default: goto handle_unusual; } // switch @@ -693,12 +724,18 @@ uint8_t* IntroductionFrame::_InternalSerialize( } // optional .location.nearby.mediums.SocketVersion socket_version = 2; - if (cached_has_bits & 0x00000002u) { + if (cached_has_bits & 0x00000004u) { target = stream->EnsureSpace(target); target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( 2, this->_internal_socket_version(), target); } + // optional string service_id_hash_salt = 3; + if (cached_has_bits & 0x00000002u) { + target = stream->WriteStringMaybeAliased( + 3, this->_internal_service_id_hash_salt(), target); + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { target = stream->WriteRaw(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).data(), static_cast(_internal_metadata_.unknown_fields(::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString).size()), target); @@ -716,7 +753,7 @@ size_t IntroductionFrame::ByteSizeLong() const { (void) cached_has_bits; cached_has_bits = _has_bits_[0]; - if (cached_has_bits & 0x00000003u) { + if (cached_has_bits & 0x00000007u) { // optional bytes service_id_hash = 1; if (cached_has_bits & 0x00000001u) { total_size += 1 + @@ -724,8 +761,15 @@ size_t IntroductionFrame::ByteSizeLong() const { this->_internal_service_id_hash()); } - // optional .location.nearby.mediums.SocketVersion socket_version = 2; + // optional string service_id_hash_salt = 3; if (cached_has_bits & 0x00000002u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_service_id_hash_salt()); + } + + // optional .location.nearby.mediums.SocketVersion socket_version = 2; + if (cached_has_bits & 0x00000004u) { total_size += 1 + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_socket_version()); } @@ -752,11 +796,14 @@ void IntroductionFrame::MergeFrom(const IntroductionFrame& from) { (void) cached_has_bits; cached_has_bits = from._has_bits_[0]; - if (cached_has_bits & 0x00000003u) { + if (cached_has_bits & 0x00000007u) { if (cached_has_bits & 0x00000001u) { _internal_set_service_id_hash(from._internal_service_id_hash()); } if (cached_has_bits & 0x00000002u) { + _internal_set_service_id_hash_salt(from._internal_service_id_hash_salt()); + } + if (cached_has_bits & 0x00000004u) { socket_version_ = from.socket_version_; } _has_bits_[0] |= cached_has_bits; @@ -786,6 +833,11 @@ void IntroductionFrame::InternalSwap(IntroductionFrame* other) { &service_id_hash_, lhs_arena, &other->service_id_hash_, rhs_arena ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &service_id_hash_salt_, lhs_arena, + &other->service_id_hash_salt_, rhs_arena + ); swap(socket_version_, other->socket_version_); } diff --git a/compiled_proto/proto/mediums/ble_frames.pb.h b/compiled_proto/proto/mediums/ble_frames.pb.h index fec28893..a57a4e39 100644 --- a/compiled_proto/proto/mediums/ble_frames.pb.h +++ b/compiled_proto/proto/mediums/ble_frames.pb.h @@ -468,6 +468,7 @@ class IntroductionFrame final : enum : int { kServiceIdHashFieldNumber = 1, + kServiceIdHashSaltFieldNumber = 3, kSocketVersionFieldNumber = 2, }; // optional bytes service_id_hash = 1; @@ -488,6 +489,24 @@ class IntroductionFrame final : std::string* _internal_mutable_service_id_hash(); public: + // optional string service_id_hash_salt = 3; + bool has_service_id_hash_salt() const; + private: + bool _internal_has_service_id_hash_salt() const; + public: + void clear_service_id_hash_salt(); + const std::string& service_id_hash_salt() const; + template + void set_service_id_hash_salt(ArgT0&& arg0, ArgT... args); + std::string* mutable_service_id_hash_salt(); + PROTOBUF_NODISCARD std::string* release_service_id_hash_salt(); + void set_allocated_service_id_hash_salt(std::string* service_id_hash_salt); + private: + const std::string& _internal_service_id_hash_salt() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_service_id_hash_salt(const std::string& value); + std::string* _internal_mutable_service_id_hash_salt(); + public: + // optional .location.nearby.mediums.SocketVersion socket_version = 2; bool has_socket_version() const; private: @@ -511,6 +530,7 @@ class IntroductionFrame final : ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr service_id_hash_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr service_id_hash_salt_; int socket_version_; friend struct ::TableStruct_proto_2fmediums_2fble_5fframes_2eproto; }; @@ -1208,7 +1228,7 @@ inline void IntroductionFrame::set_allocated_service_id_hash(std::string* servic // optional .location.nearby.mediums.SocketVersion socket_version = 2; inline bool IntroductionFrame::_internal_has_socket_version() const { - bool value = (_has_bits_[0] & 0x00000002u) != 0; + bool value = (_has_bits_[0] & 0x00000004u) != 0; return value; } inline bool IntroductionFrame::has_socket_version() const { @@ -1216,7 +1236,7 @@ inline bool IntroductionFrame::has_socket_version() const { } inline void IntroductionFrame::clear_socket_version() { socket_version_ = 0; - _has_bits_[0] &= ~0x00000002u; + _has_bits_[0] &= ~0x00000004u; } inline ::location::nearby::mediums::SocketVersion IntroductionFrame::_internal_socket_version() const { return static_cast< ::location::nearby::mediums::SocketVersion >(socket_version_); @@ -1227,7 +1247,7 @@ inline ::location::nearby::mediums::SocketVersion IntroductionFrame::socket_vers } inline void IntroductionFrame::_internal_set_socket_version(::location::nearby::mediums::SocketVersion value) { assert(::location::nearby::mediums::SocketVersion_IsValid(value)); - _has_bits_[0] |= 0x00000002u; + _has_bits_[0] |= 0x00000004u; socket_version_ = value; } inline void IntroductionFrame::set_socket_version(::location::nearby::mediums::SocketVersion value) { @@ -1235,6 +1255,75 @@ inline void IntroductionFrame::set_socket_version(::location::nearby::mediums::S // @@protoc_insertion_point(field_set:location.nearby.mediums.IntroductionFrame.socket_version) } +// optional string service_id_hash_salt = 3; +inline bool IntroductionFrame::_internal_has_service_id_hash_salt() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool IntroductionFrame::has_service_id_hash_salt() const { + return _internal_has_service_id_hash_salt(); +} +inline void IntroductionFrame::clear_service_id_hash_salt() { + service_id_hash_salt_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000002u; +} +inline const std::string& IntroductionFrame::service_id_hash_salt() const { + // @@protoc_insertion_point(field_get:location.nearby.mediums.IntroductionFrame.service_id_hash_salt) + return _internal_service_id_hash_salt(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void IntroductionFrame::set_service_id_hash_salt(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000002u; + service_id_hash_salt_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:location.nearby.mediums.IntroductionFrame.service_id_hash_salt) +} +inline std::string* IntroductionFrame::mutable_service_id_hash_salt() { + std::string* _s = _internal_mutable_service_id_hash_salt(); + // @@protoc_insertion_point(field_mutable:location.nearby.mediums.IntroductionFrame.service_id_hash_salt) + return _s; +} +inline const std::string& IntroductionFrame::_internal_service_id_hash_salt() const { + return service_id_hash_salt_.Get(); +} +inline void IntroductionFrame::_internal_set_service_id_hash_salt(const std::string& value) { + _has_bits_[0] |= 0x00000002u; + service_id_hash_salt_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* IntroductionFrame::_internal_mutable_service_id_hash_salt() { + _has_bits_[0] |= 0x00000002u; + return service_id_hash_salt_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* IntroductionFrame::release_service_id_hash_salt() { + // @@protoc_insertion_point(field_release:location.nearby.mediums.IntroductionFrame.service_id_hash_salt) + if (!_internal_has_service_id_hash_salt()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000002u; + auto* p = service_id_hash_salt_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (service_id_hash_salt_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + service_id_hash_salt_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void IntroductionFrame::set_allocated_service_id_hash_salt(std::string* service_id_hash_salt) { + if (service_id_hash_salt != nullptr) { + _has_bits_[0] |= 0x00000002u; + } else { + _has_bits_[0] &= ~0x00000002u; + } + service_id_hash_salt_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), service_id_hash_salt, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (service_id_hash_salt_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + service_id_hash_salt_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:location.nearby.mediums.IntroductionFrame.service_id_hash_salt) +} + // ------------------------------------------------------------------- // DisconnectionFrame diff --git a/compiled_proto/proto/sharing_enums.pb.cc b/compiled_proto/proto/sharing_enums.pb.cc index 0c7a9b0b..9b200e94 100644 --- a/compiled_proto/proto/sharing_enums.pb.cc +++ b/compiled_proto/proto/sharing_enums.pb.cc @@ -86,13 +86,18 @@ bool EventType_IsValid(int value) { case 57: case 58: case 59: + case 60: + case 61: + case 62: + case 63: + case 64: return true; default: return false; } } -static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed EventType_strings[60] = {}; +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed EventType_strings[65] = {}; static const char EventType_names[] = "ACCEPT_AGREEMENTS" @@ -107,6 +112,7 @@ static const char EventType_names[] = "CANCEL_RECEIVING_ATTACHMENTS" "CANCEL_SENDING_ATTACHMENTS" "DECLINE_AGREEMENTS" + "DECRYPT_CERTIFICATE_FAILURE" "DEFAULT_OPT_IN" "DESCRIBE_ATTACHMENTS" "DEVICE_SETTINGS" @@ -117,6 +123,7 @@ static const char EventType_names[] = "DISPLAY_PRIVACY_NOTIFICATION" "ENABLE_NEARBY_SHARING" "ESTABLISH_CONNECTION" + "FAST_INIT_DISCOVER_DEVICE" "FAST_SHARE_SERVER_RESPONSE" "INSTALL_APK" "LAUNCH_ACTIVITY" @@ -124,6 +131,7 @@ static const char EventType_names[] = "LAUNCH_PHONE_CONSENT" "LAUNCH_SETUP_ACTIVITY" "OPEN_RECEIVED_ATTACHMENTS" + "PARSING_FAILED_ENDPOINT_ID" "PREFERENCES_USAGE" "PROCESS_RECEIVED_ATTACHMENTS_END" "QR_CODE_LINK_SHOWN" @@ -139,10 +147,12 @@ static const char EventType_names[] = "SCAN_FOR_SHARE_TARGETS_START" "SEND_ATTACHMENTS_END" "SEND_ATTACHMENTS_START" + "SEND_DESKTOP_NOTIFICATION" "SEND_FAST_INITIALIZATION" "SEND_INTRODUCTION" "SEND_START" "SETUP_WIZARD" + "SET_ACCOUNT" "SET_DATA_USAGE" "SET_DEVICE_NAME" "SET_VISIBILITY" @@ -169,117 +179,127 @@ static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry EventType_entries[] = { {EventType_names + 195, 28}, 20 }, { {EventType_names + 223, 26}, 19 }, { {EventType_names + 249, 18}, 46 }, - { {EventType_names + 267, 14}, 56 }, - { {EventType_names + 281, 20}, 4 }, - { {EventType_names + 301, 15}, 49 }, - { {EventType_names + 316, 21}, 11 }, - { {EventType_names + 337, 27}, 29 }, - { {EventType_names + 364, 28}, 32 }, - { {EventType_names + 392, 21}, 54 }, - { {EventType_names + 413, 28}, 53 }, - { {EventType_names + 441, 21}, 2 }, - { {EventType_names + 462, 20}, 48 }, - { {EventType_names + 482, 26}, 25 }, - { {EventType_names + 508, 11}, 40 }, - { {EventType_names + 519, 15}, 31 }, - { {EventType_names + 534, 14}, 42 }, - { {EventType_names + 548, 20}, 38 }, - { {EventType_names + 568, 21}, 22 }, - { {EventType_names + 589, 25}, 21 }, - { {EventType_names + 614, 17}, 55 }, - { {EventType_names + 631, 32}, 43 }, - { {EventType_names + 663, 18}, 59 }, - { {EventType_names + 681, 23}, 18 }, - { {EventType_names + 704, 25}, 17 }, - { {EventType_names + 729, 27}, 10 }, - { {EventType_names + 756, 20}, 13 }, - { {EventType_names + 776, 14}, 24 }, - { {EventType_names + 790, 26}, 37 }, - { {EventType_names + 816, 27}, 47 }, - { {EventType_names + 843, 23}, 14 }, - { {EventType_names + 866, 26}, 6 }, - { {EventType_names + 892, 28}, 5 }, - { {EventType_names + 920, 20}, 16 }, - { {EventType_names + 940, 22}, 15 }, - { {EventType_names + 962, 24}, 9 }, - { {EventType_names + 986, 17}, 12 }, - { {EventType_names + 1003, 10}, 26 }, - { {EventType_names + 1013, 12}, 57 }, - { {EventType_names + 1025, 14}, 28 }, - { {EventType_names + 1039, 15}, 45 }, - { {EventType_names + 1054, 14}, 3 }, - { {EventType_names + 1068, 12}, 35 }, - { {EventType_names + 1080, 8}, 34 }, - { {EventType_names + 1088, 24}, 33 }, - { {EventType_names + 1112, 11}, 58 }, - { {EventType_names + 1123, 29}, 52 }, - { {EventType_names + 1152, 23}, 39 }, - { {EventType_names + 1175, 24}, 44 }, - { {EventType_names + 1199, 18}, 0 }, - { {EventType_names + 1217, 10}, 41 }, + { {EventType_names + 267, 27}, 64 }, + { {EventType_names + 294, 14}, 56 }, + { {EventType_names + 308, 20}, 4 }, + { {EventType_names + 328, 15}, 49 }, + { {EventType_names + 343, 21}, 11 }, + { {EventType_names + 364, 27}, 29 }, + { {EventType_names + 391, 28}, 32 }, + { {EventType_names + 419, 21}, 54 }, + { {EventType_names + 440, 28}, 53 }, + { {EventType_names + 468, 21}, 2 }, + { {EventType_names + 489, 20}, 48 }, + { {EventType_names + 509, 25}, 61 }, + { {EventType_names + 534, 26}, 25 }, + { {EventType_names + 560, 11}, 40 }, + { {EventType_names + 571, 15}, 31 }, + { {EventType_names + 586, 14}, 42 }, + { {EventType_names + 600, 20}, 38 }, + { {EventType_names + 620, 21}, 22 }, + { {EventType_names + 641, 25}, 21 }, + { {EventType_names + 666, 26}, 60 }, + { {EventType_names + 692, 17}, 55 }, + { {EventType_names + 709, 32}, 43 }, + { {EventType_names + 741, 18}, 59 }, + { {EventType_names + 759, 23}, 18 }, + { {EventType_names + 782, 25}, 17 }, + { {EventType_names + 807, 27}, 10 }, + { {EventType_names + 834, 20}, 13 }, + { {EventType_names + 854, 14}, 24 }, + { {EventType_names + 868, 26}, 37 }, + { {EventType_names + 894, 27}, 47 }, + { {EventType_names + 921, 23}, 14 }, + { {EventType_names + 944, 26}, 6 }, + { {EventType_names + 970, 28}, 5 }, + { {EventType_names + 998, 20}, 16 }, + { {EventType_names + 1018, 22}, 15 }, + { {EventType_names + 1040, 25}, 62 }, + { {EventType_names + 1065, 24}, 9 }, + { {EventType_names + 1089, 17}, 12 }, + { {EventType_names + 1106, 10}, 26 }, + { {EventType_names + 1116, 12}, 57 }, + { {EventType_names + 1128, 11}, 63 }, + { {EventType_names + 1139, 14}, 28 }, + { {EventType_names + 1153, 15}, 45 }, + { {EventType_names + 1168, 14}, 3 }, + { {EventType_names + 1182, 12}, 35 }, + { {EventType_names + 1194, 8}, 34 }, + { {EventType_names + 1202, 24}, 33 }, + { {EventType_names + 1226, 11}, 58 }, + { {EventType_names + 1237, 29}, 52 }, + { {EventType_names + 1266, 23}, 39 }, + { {EventType_names + 1289, 24}, 44 }, + { {EventType_names + 1313, 18}, 0 }, + { {EventType_names + 1331, 10}, 41 }, }; static const int EventType_entries_by_number[] = { - 58, // 0 -> UNKNOWN_EVENT_TYPE + 63, // 0 -> UNKNOWN_EVENT_TYPE 0, // 1 -> ACCEPT_AGREEMENTS - 20, // 2 -> ENABLE_NEARBY_SHARING - 50, // 3 -> SET_VISIBILITY - 13, // 4 -> DESCRIBE_ATTACHMENTS - 41, // 5 -> SCAN_FOR_SHARE_TARGETS_START - 40, // 6 -> SCAN_FOR_SHARE_TARGETS_END + 21, // 2 -> ENABLE_NEARBY_SHARING + 55, // 3 -> SET_VISIBILITY + 14, // 4 -> DESCRIBE_ATTACHMENTS + 44, // 5 -> SCAN_FOR_SHARE_TARGETS_START + 43, // 6 -> SCAN_FOR_SHARE_TARGETS_END 5, // 7 -> ADVERTISE_DEVICE_PRESENCE_START 4, // 8 -> ADVERTISE_DEVICE_PRESENCE_END - 44, // 9 -> SEND_FAST_INITIALIZATION - 34, // 10 -> RECEIVE_FAST_INITIALIZATION - 15, // 11 -> DISCOVER_SHARE_TARGET - 45, // 12 -> SEND_INTRODUCTION - 35, // 13 -> RECEIVE_INTRODUCTION - 39, // 14 -> RESPOND_TO_INTRODUCTION - 43, // 15 -> SEND_ATTACHMENTS_START - 42, // 16 -> SEND_ATTACHMENTS_END - 33, // 17 -> RECEIVE_ATTACHMENTS_START - 32, // 18 -> RECEIVE_ATTACHMENTS_END + 48, // 9 -> SEND_FAST_INITIALIZATION + 37, // 10 -> RECEIVE_FAST_INITIALIZATION + 16, // 11 -> DISCOVER_SHARE_TARGET + 49, // 12 -> SEND_INTRODUCTION + 38, // 13 -> RECEIVE_INTRODUCTION + 42, // 14 -> RESPOND_TO_INTRODUCTION + 46, // 15 -> SEND_ATTACHMENTS_START + 45, // 16 -> SEND_ATTACHMENTS_END + 36, // 17 -> RECEIVE_ATTACHMENTS_START + 35, // 18 -> RECEIVE_ATTACHMENTS_END 10, // 19 -> CANCEL_SENDING_ATTACHMENTS 9, // 20 -> CANCEL_RECEIVING_ATTACHMENTS - 28, // 21 -> OPEN_RECEIVED_ATTACHMENTS - 27, // 22 -> LAUNCH_SETUP_ACTIVITY + 30, // 21 -> OPEN_RECEIVED_ATTACHMENTS + 29, // 22 -> LAUNCH_SETUP_ACTIVITY 2, // 23 -> ADD_CONTACT - 36, // 24 -> REMOVE_CONTACT - 22, // 25 -> FAST_SHARE_SERVER_RESPONSE - 46, // 26 -> SEND_START + 39, // 24 -> REMOVE_CONTACT + 24, // 25 -> FAST_SHARE_SERVER_RESPONSE + 50, // 26 -> SEND_START 1, // 27 -> ACCEPT_FAST_INITIALIZATION - 48, // 28 -> SET_DATA_USAGE - 16, // 29 -> DISMISS_FAST_INITIALIZATION + 53, // 28 -> SET_DATA_USAGE + 17, // 29 -> DISMISS_FAST_INITIALIZATION 8, // 30 -> CANCEL_CONNECTION - 24, // 31 -> LAUNCH_ACTIVITY - 17, // 32 -> DISMISS_PRIVACY_NOTIFICATION - 53, // 33 -> TAP_PRIVACY_NOTIFICATION - 52, // 34 -> TAP_HELP - 51, // 35 -> TAP_FEEDBACK + 26, // 31 -> LAUNCH_ACTIVITY + 18, // 32 -> DISMISS_PRIVACY_NOTIFICATION + 58, // 33 -> TAP_PRIVACY_NOTIFICATION + 57, // 34 -> TAP_HELP + 56, // 35 -> TAP_FEEDBACK 3, // 36 -> ADD_QUICK_SETTINGS_TILE - 37, // 37 -> REMOVE_QUICK_SETTINGS_TILE - 26, // 38 -> LAUNCH_PHONE_CONSENT - 56, // 39 -> TAP_QUICK_SETTINGS_TILE - 23, // 40 -> INSTALL_APK - 59, // 41 -> VERIFY_APK - 25, // 42 -> LAUNCH_CONSENT - 30, // 43 -> PROCESS_RECEIVED_ATTACHMENTS_END - 57, // 44 -> TOGGLE_SHOW_NOTIFICATION - 49, // 45 -> SET_DEVICE_NAME + 40, // 37 -> REMOVE_QUICK_SETTINGS_TILE + 28, // 38 -> LAUNCH_PHONE_CONSENT + 61, // 39 -> TAP_QUICK_SETTINGS_TILE + 25, // 40 -> INSTALL_APK + 64, // 41 -> VERIFY_APK + 27, // 42 -> LAUNCH_CONSENT + 33, // 43 -> PROCESS_RECEIVED_ATTACHMENTS_END + 62, // 44 -> TOGGLE_SHOW_NOTIFICATION + 54, // 45 -> SET_DEVICE_NAME 11, // 46 -> DECLINE_AGREEMENTS - 38, // 47 -> REQUEST_SETTING_PERMISSIONS - 21, // 48 -> ESTABLISH_CONNECTION - 14, // 49 -> DEVICE_SETTINGS + 41, // 47 -> REQUEST_SETTING_PERMISSIONS + 22, // 48 -> ESTABLISH_CONNECTION + 15, // 49 -> DEVICE_SETTINGS 7, // 50 -> AUTO_DISMISS_FAST_INITIALIZATION 6, // 51 -> APP_CRASH - 55, // 52 -> TAP_QUICK_SETTINGS_FILE_SHARE - 19, // 53 -> DISPLAY_PRIVACY_NOTIFICATION - 18, // 54 -> DISPLAY_PHONE_CONSENT - 29, // 55 -> PREFERENCES_USAGE - 12, // 56 -> DEFAULT_OPT_IN - 47, // 57 -> SETUP_WIZARD - 54, // 58 -> TAP_QR_CODE - 31, // 59 -> QR_CODE_LINK_SHOWN + 60, // 52 -> TAP_QUICK_SETTINGS_FILE_SHARE + 20, // 53 -> DISPLAY_PRIVACY_NOTIFICATION + 19, // 54 -> DISPLAY_PHONE_CONSENT + 32, // 55 -> PREFERENCES_USAGE + 13, // 56 -> DEFAULT_OPT_IN + 51, // 57 -> SETUP_WIZARD + 59, // 58 -> TAP_QR_CODE + 34, // 59 -> QR_CODE_LINK_SHOWN + 31, // 60 -> PARSING_FAILED_ENDPOINT_ID + 23, // 61 -> FAST_INIT_DISCOVER_DEVICE + 47, // 62 -> SEND_DESKTOP_NOTIFICATION + 52, // 63 -> SET_ACCOUNT + 12, // 64 -> DECRYPT_CERTIFICATE_FAILURE }; const std::string& EventType_Name( @@ -288,12 +308,12 @@ const std::string& EventType_Name( ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( EventType_entries, EventType_entries_by_number, - 60, EventType_strings); + 65, EventType_strings); (void) dummy; int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( EventType_entries, EventType_entries_by_number, - 60, value); + 65, value); return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : EventType_strings[idx].get(); } @@ -301,7 +321,7 @@ bool EventType_Parse( ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, EventType* value) { int int_value; bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( - EventType_entries, 60, name, &int_value); + EventType_entries, 65, name, &int_value); if (success) { *value = static_cast(int_value); } @@ -626,13 +646,17 @@ bool AttachmentTransmissionStatus_IsValid(int value) { case 15: case 16: case 17: + case 18: + case 19: + case 20: + case 21: return true; default: return false; } } -static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed AttachmentTransmissionStatus_strings[18] = {}; +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed AttachmentTransmissionStatus_strings[22] = {}; static const char AttachmentTransmissionStatus_names[] = "AWAITING_REMOTE_ACCEPTANCE_FAILED_ATTACHMENT" @@ -643,6 +667,10 @@ static const char AttachmentTransmissionStatus_names[] = "FAILED_NO_SHARE_TARGET_ENDPOINT" "FAILED_NO_TRANSFER_UPDATE_CALLBACK" "FAILED_NULL_CONNECTION" + "FAILED_NULL_CONNECTION_DISCONNECTED" + "FAILED_NULL_CONNECTION_FAILURE" + "FAILED_NULL_CONNECTION_INIT_OUTGOING" + "FAILED_NULL_CONNECTION_LOST_CONNECTIVITY" "FAILED_PAIRED_KEYHANDSHAKE" "FAILED_UNKNOWN_REMOTE_RESPONSE" "FAILED_WRITE_INTRODUCTION" @@ -663,37 +691,45 @@ static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry AttachmentTransmission { {AttachmentTransmissionStatus_names + 176, 31}, 12 }, { {AttachmentTransmissionStatus_names + 207, 34}, 8 }, { {AttachmentTransmissionStatus_names + 241, 22}, 14 }, - { {AttachmentTransmissionStatus_names + 263, 26}, 13 }, - { {AttachmentTransmissionStatus_names + 289, 30}, 17 }, - { {AttachmentTransmissionStatus_names + 319, 25}, 16 }, - { {AttachmentTransmissionStatus_names + 344, 28}, 9 }, - { {AttachmentTransmissionStatus_names + 372, 27}, 7 }, - { {AttachmentTransmissionStatus_names + 399, 19}, 11 }, - { {AttachmentTransmissionStatus_names + 418, 19}, 4 }, - { {AttachmentTransmissionStatus_names + 437, 20}, 5 }, - { {AttachmentTransmissionStatus_names + 457, 38}, 0 }, - { {AttachmentTransmissionStatus_names + 495, 38}, 10 }, + { {AttachmentTransmissionStatus_names + 263, 35}, 19 }, + { {AttachmentTransmissionStatus_names + 298, 30}, 21 }, + { {AttachmentTransmissionStatus_names + 328, 36}, 18 }, + { {AttachmentTransmissionStatus_names + 364, 40}, 20 }, + { {AttachmentTransmissionStatus_names + 404, 26}, 13 }, + { {AttachmentTransmissionStatus_names + 430, 30}, 17 }, + { {AttachmentTransmissionStatus_names + 460, 25}, 16 }, + { {AttachmentTransmissionStatus_names + 485, 28}, 9 }, + { {AttachmentTransmissionStatus_names + 513, 27}, 7 }, + { {AttachmentTransmissionStatus_names + 540, 19}, 11 }, + { {AttachmentTransmissionStatus_names + 559, 19}, 4 }, + { {AttachmentTransmissionStatus_names + 578, 20}, 5 }, + { {AttachmentTransmissionStatus_names + 598, 38}, 0 }, + { {AttachmentTransmissionStatus_names + 636, 38}, 10 }, }; static const int AttachmentTransmissionStatus_entries_by_number[] = { - 16, // 0 -> UNKNOWN_ATTACHMENT_TRANSMISSION_STATUS + 20, // 0 -> UNKNOWN_ATTACHMENT_TRANSMISSION_STATUS 2, // 1 -> COMPLETE_ATTACHMENT_TRANSMISSION_STATUS 1, // 2 -> CANCELED_ATTACHMENT_TRANSMISSION_STATUS 3, // 3 -> FAILED_ATTACHMENT_TRANSMISSION_STATUS - 14, // 4 -> REJECTED_ATTACHMENT - 15, // 5 -> TIMED_OUT_ATTACHMENT + 18, // 4 -> REJECTED_ATTACHMENT + 19, // 5 -> TIMED_OUT_ATTACHMENT 0, // 6 -> AWAITING_REMOTE_ACCEPTANCE_FAILED_ATTACHMENT - 12, // 7 -> NOT_ENOUGH_SPACE_ATTACHMENT + 16, // 7 -> NOT_ENOUGH_SPACE_ATTACHMENT 6, // 8 -> FAILED_NO_TRANSFER_UPDATE_CALLBACK - 11, // 9 -> MEDIA_UNAVAILABLE_ATTACHMENT - 17, // 10 -> UNSUPPORTED_ATTACHMENT_TYPE_ATTACHMENT - 13, // 11 -> NO_ATTACHMENT_FOUND + 15, // 9 -> MEDIA_UNAVAILABLE_ATTACHMENT + 21, // 10 -> UNSUPPORTED_ATTACHMENT_TYPE_ATTACHMENT + 17, // 11 -> NO_ATTACHMENT_FOUND 5, // 12 -> FAILED_NO_SHARE_TARGET_ENDPOINT - 8, // 13 -> FAILED_PAIRED_KEYHANDSHAKE + 12, // 13 -> FAILED_PAIRED_KEYHANDSHAKE 7, // 14 -> FAILED_NULL_CONNECTION 4, // 15 -> FAILED_NO_PAYLOAD - 10, // 16 -> FAILED_WRITE_INTRODUCTION - 9, // 17 -> FAILED_UNKNOWN_REMOTE_RESPONSE + 14, // 16 -> FAILED_WRITE_INTRODUCTION + 13, // 17 -> FAILED_UNKNOWN_REMOTE_RESPONSE + 10, // 18 -> FAILED_NULL_CONNECTION_INIT_OUTGOING + 8, // 19 -> FAILED_NULL_CONNECTION_DISCONNECTED + 11, // 20 -> FAILED_NULL_CONNECTION_LOST_CONNECTIVITY + 9, // 21 -> FAILED_NULL_CONNECTION_FAILURE }; const std::string& AttachmentTransmissionStatus_Name( @@ -702,12 +738,12 @@ const std::string& AttachmentTransmissionStatus_Name( ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( AttachmentTransmissionStatus_entries, AttachmentTransmissionStatus_entries_by_number, - 18, AttachmentTransmissionStatus_strings); + 22, AttachmentTransmissionStatus_strings); (void) dummy; int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( AttachmentTransmissionStatus_entries, AttachmentTransmissionStatus_entries_by_number, - 18, value); + 22, value); return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : AttachmentTransmissionStatus_strings[idx].get(); } @@ -715,12 +751,131 @@ bool AttachmentTransmissionStatus_Parse( ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, AttachmentTransmissionStatus* value) { int int_value; bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( - AttachmentTransmissionStatus_entries, 18, name, &int_value); + AttachmentTransmissionStatus_entries, 22, name, &int_value); if (success) { *value = static_cast(int_value); } return success; } +bool ConnectionLayerStatus_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed ConnectionLayerStatus_strings[19] = {}; + +static const char ConnectionLayerStatus_names[] = + "CONNECTION_LAYER_STATUS_ALREADY_ADVERTISING" + "CONNECTION_LAYER_STATUS_ALREADY_CONNECTED_TO_END_POINT" + "CONNECTION_LAYER_STATUS_ALREADY_DISCOVERING" + "CONNECTION_LAYER_STATUS_ALREADY_HAVE_ACTIVE_STRATEGY" + "CONNECTION_LAYER_STATUS_ALREADY_LISTENING" + "CONNECTION_LAYER_STATUS_BLE_ERROR" + "CONNECTION_LAYER_STATUS_BLUETOOTH_ERROR" + "CONNECTION_LAYER_STATUS_CONNECTION_REJECTED" + "CONNECTION_LAYER_STATUS_END_POINT_IO_ERROR" + "CONNECTION_LAYER_STATUS_END_POINT_UNKNOWN" + "CONNECTION_LAYER_STATUS_ERROR" + "CONNECTION_LAYER_STATUS_NOT_CONNECTED_TO_END_POINT" + "CONNECTION_LAYER_STATUS_OUT_OF_ORDER_API_CALL" + "CONNECTION_LAYER_STATUS_PAYLOAD_UNKNOWN" + "CONNECTION_LAYER_STATUS_RESET" + "CONNECTION_LAYER_STATUS_SUCCESS" + "CONNECTION_LAYER_STATUS_TIMEOUT" + "CONNECTION_LAYER_STATUS_UNKNOWN" + "CONNECTION_LAYER_STATUS_WIFI_LAN_ERROR"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry ConnectionLayerStatus_entries[] = { + { {ConnectionLayerStatus_names + 0, 43}, 5 }, + { {ConnectionLayerStatus_names + 43, 54}, 11 }, + { {ConnectionLayerStatus_names + 97, 43}, 6 }, + { {ConnectionLayerStatus_names + 140, 52}, 4 }, + { {ConnectionLayerStatus_names + 192, 41}, 7 }, + { {ConnectionLayerStatus_names + 233, 33}, 14 }, + { {ConnectionLayerStatus_names + 266, 39}, 13 }, + { {ConnectionLayerStatus_names + 305, 43}, 10 }, + { {ConnectionLayerStatus_names + 348, 42}, 8 }, + { {ConnectionLayerStatus_names + 390, 41}, 9 }, + { {ConnectionLayerStatus_names + 431, 29}, 2 }, + { {ConnectionLayerStatus_names + 460, 50}, 12 }, + { {ConnectionLayerStatus_names + 510, 45}, 3 }, + { {ConnectionLayerStatus_names + 555, 39}, 16 }, + { {ConnectionLayerStatus_names + 594, 29}, 17 }, + { {ConnectionLayerStatus_names + 623, 31}, 1 }, + { {ConnectionLayerStatus_names + 654, 31}, 18 }, + { {ConnectionLayerStatus_names + 685, 31}, 0 }, + { {ConnectionLayerStatus_names + 716, 38}, 15 }, +}; + +static const int ConnectionLayerStatus_entries_by_number[] = { + 17, // 0 -> CONNECTION_LAYER_STATUS_UNKNOWN + 15, // 1 -> CONNECTION_LAYER_STATUS_SUCCESS + 10, // 2 -> CONNECTION_LAYER_STATUS_ERROR + 12, // 3 -> CONNECTION_LAYER_STATUS_OUT_OF_ORDER_API_CALL + 3, // 4 -> CONNECTION_LAYER_STATUS_ALREADY_HAVE_ACTIVE_STRATEGY + 0, // 5 -> CONNECTION_LAYER_STATUS_ALREADY_ADVERTISING + 2, // 6 -> CONNECTION_LAYER_STATUS_ALREADY_DISCOVERING + 4, // 7 -> CONNECTION_LAYER_STATUS_ALREADY_LISTENING + 8, // 8 -> CONNECTION_LAYER_STATUS_END_POINT_IO_ERROR + 9, // 9 -> CONNECTION_LAYER_STATUS_END_POINT_UNKNOWN + 7, // 10 -> CONNECTION_LAYER_STATUS_CONNECTION_REJECTED + 1, // 11 -> CONNECTION_LAYER_STATUS_ALREADY_CONNECTED_TO_END_POINT + 11, // 12 -> CONNECTION_LAYER_STATUS_NOT_CONNECTED_TO_END_POINT + 6, // 13 -> CONNECTION_LAYER_STATUS_BLUETOOTH_ERROR + 5, // 14 -> CONNECTION_LAYER_STATUS_BLE_ERROR + 18, // 15 -> CONNECTION_LAYER_STATUS_WIFI_LAN_ERROR + 13, // 16 -> CONNECTION_LAYER_STATUS_PAYLOAD_UNKNOWN + 14, // 17 -> CONNECTION_LAYER_STATUS_RESET + 16, // 18 -> CONNECTION_LAYER_STATUS_TIMEOUT +}; + +const std::string& ConnectionLayerStatus_Name( + ConnectionLayerStatus value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + ConnectionLayerStatus_entries, + ConnectionLayerStatus_entries_by_number, + 19, ConnectionLayerStatus_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + ConnectionLayerStatus_entries, + ConnectionLayerStatus_entries_by_number, + 19, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + ConnectionLayerStatus_strings[idx].get(); +} +bool ConnectionLayerStatus_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ConnectionLayerStatus* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + ConnectionLayerStatus_entries, 19, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} bool ProcessReceivedAttachmentsStatus_IsValid(int value) { switch (value) { case 0: @@ -1328,6 +1483,168 @@ bool ServerResponseState_Parse( } return success; } +bool SyncPurpose_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed SyncPurpose_strings[16] = {}; + +static const char SyncPurpose_names[] = + "SYNC_PURPOSE_ACCOUNT_CHANGE" + "SYNC_PURPOSE_CHECK_DEFAULT_OPT_IN" + "SYNC_PURPOSE_CHIME_NOTIFICATION" + "SYNC_PURPOSE_CONTACT_LIST_CHANGE" + "SYNC_PURPOSE_DAILY_SYNC" + "SYNC_PURPOSE_NEARBY_SHARE_ENABLED" + "SYNC_PURPOSE_ON_DEMAND_SYNC" + "SYNC_PURPOSE_OPT_IN_FIRST_SYNC" + "SYNC_PURPOSE_REGULAR_CHECK_CONTACT_REACHABILITY" + "SYNC_PURPOSE_SHOW_C11N_VIEW" + "SYNC_PURPOSE_SYNC_AT_ADVERTISEMENT" + "SYNC_PURPOSE_SYNC_AT_DISCOVERY" + "SYNC_PURPOSE_SYNC_AT_FAST_INIT" + "SYNC_PURPOSE_SYNC_AT_LOAD_PRIVATE_CERTIFICATE" + "SYNC_PURPOSE_UNKNOWN" + "SYNC_PURPOSE_VISIBILITY_SELECTED_CONTACT_CHANGE"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry SyncPurpose_entries[] = { + { {SyncPurpose_names + 0, 27}, 15 }, + { {SyncPurpose_names + 27, 33}, 5 }, + { {SyncPurpose_names + 60, 31}, 2 }, + { {SyncPurpose_names + 91, 32}, 11 }, + { {SyncPurpose_names + 123, 23}, 3 }, + { {SyncPurpose_names + 146, 33}, 6 }, + { {SyncPurpose_names + 179, 27}, 1 }, + { {SyncPurpose_names + 206, 30}, 4 }, + { {SyncPurpose_names + 236, 47}, 13 }, + { {SyncPurpose_names + 283, 27}, 12 }, + { {SyncPurpose_names + 310, 34}, 10 }, + { {SyncPurpose_names + 344, 30}, 8 }, + { {SyncPurpose_names + 374, 30}, 7 }, + { {SyncPurpose_names + 404, 45}, 9 }, + { {SyncPurpose_names + 449, 20}, 0 }, + { {SyncPurpose_names + 469, 47}, 14 }, +}; + +static const int SyncPurpose_entries_by_number[] = { + 14, // 0 -> SYNC_PURPOSE_UNKNOWN + 6, // 1 -> SYNC_PURPOSE_ON_DEMAND_SYNC + 2, // 2 -> SYNC_PURPOSE_CHIME_NOTIFICATION + 4, // 3 -> SYNC_PURPOSE_DAILY_SYNC + 7, // 4 -> SYNC_PURPOSE_OPT_IN_FIRST_SYNC + 1, // 5 -> SYNC_PURPOSE_CHECK_DEFAULT_OPT_IN + 5, // 6 -> SYNC_PURPOSE_NEARBY_SHARE_ENABLED + 12, // 7 -> SYNC_PURPOSE_SYNC_AT_FAST_INIT + 11, // 8 -> SYNC_PURPOSE_SYNC_AT_DISCOVERY + 13, // 9 -> SYNC_PURPOSE_SYNC_AT_LOAD_PRIVATE_CERTIFICATE + 10, // 10 -> SYNC_PURPOSE_SYNC_AT_ADVERTISEMENT + 3, // 11 -> SYNC_PURPOSE_CONTACT_LIST_CHANGE + 9, // 12 -> SYNC_PURPOSE_SHOW_C11N_VIEW + 8, // 13 -> SYNC_PURPOSE_REGULAR_CHECK_CONTACT_REACHABILITY + 15, // 14 -> SYNC_PURPOSE_VISIBILITY_SELECTED_CONTACT_CHANGE + 0, // 15 -> SYNC_PURPOSE_ACCOUNT_CHANGE +}; + +const std::string& SyncPurpose_Name( + SyncPurpose value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + SyncPurpose_entries, + SyncPurpose_entries_by_number, + 16, SyncPurpose_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + SyncPurpose_entries, + SyncPurpose_entries_by_number, + 16, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + SyncPurpose_strings[idx].get(); +} +bool SyncPurpose_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, SyncPurpose* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + SyncPurpose_entries, 16, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} +bool ClientRole_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed ClientRole_strings[3] = {}; + +static const char ClientRole_names[] = + "CLIENT_ROLE_RECEIVER" + "CLIENT_ROLE_SENDER" + "CLIENT_ROLE_UNKNOWN"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry ClientRole_entries[] = { + { {ClientRole_names + 0, 20}, 2 }, + { {ClientRole_names + 20, 18}, 1 }, + { {ClientRole_names + 38, 19}, 0 }, +}; + +static const int ClientRole_entries_by_number[] = { + 2, // 0 -> CLIENT_ROLE_UNKNOWN + 1, // 1 -> CLIENT_ROLE_SENDER + 0, // 2 -> CLIENT_ROLE_RECEIVER +}; + +const std::string& ClientRole_Name( + ClientRole value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + ClientRole_entries, + ClientRole_entries_by_number, + 3, ClientRole_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + ClientRole_entries, + ClientRole_entries_by_number, + 3, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + ClientRole_strings[idx].get(); +} +bool ClientRole_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ClientRole* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + ClientRole_entries, 3, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} bool ScanType_IsValid(int value) { switch (value) { case 0: @@ -1391,6 +1708,61 @@ bool ScanType_Parse( } return success; } +bool ParsingFailedType_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed ParsingFailedType_strings[3] = {}; + +static const char ParsingFailedType_names[] = + "FAILED_CONVERT_SHARE_TARGET" + "FAILED_PARSE_ADVERTISEMENT" + "FAILED_UNKNOWN_TYPE"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry ParsingFailedType_entries[] = { + { {ParsingFailedType_names + 0, 27}, 2 }, + { {ParsingFailedType_names + 27, 26}, 1 }, + { {ParsingFailedType_names + 53, 19}, 0 }, +}; + +static const int ParsingFailedType_entries_by_number[] = { + 2, // 0 -> FAILED_UNKNOWN_TYPE + 1, // 1 -> FAILED_PARSE_ADVERTISEMENT + 0, // 2 -> FAILED_CONVERT_SHARE_TARGET +}; + +const std::string& ParsingFailedType_Name( + ParsingFailedType value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + ParsingFailedType_entries, + ParsingFailedType_entries_by_number, + 3, ParsingFailedType_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + ParsingFailedType_entries, + ParsingFailedType_entries_by_number, + 3, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + ParsingFailedType_strings[idx].get(); +} +bool ParsingFailedType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ParsingFailedType* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + ParsingFailedType_entries, 3, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} bool AdvertisingMode_IsValid(int value) { switch (value) { case 0: @@ -2348,6 +2720,341 @@ bool PreferencesActionStatus_Parse( } return success; } +bool FastInitState_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed FastInitState_strings[4] = {}; + +static const char FastInitState_names[] = + "FAST_INIT_CLOSE_STATE" + "FAST_INIT_FAR_STATE" + "FAST_INIT_LOST_STATE" + "FAST_INIT_UNKNOWN_STATE"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry FastInitState_entries[] = { + { {FastInitState_names + 0, 21}, 1 }, + { {FastInitState_names + 21, 19}, 2 }, + { {FastInitState_names + 40, 20}, 3 }, + { {FastInitState_names + 60, 23}, 0 }, +}; + +static const int FastInitState_entries_by_number[] = { + 3, // 0 -> FAST_INIT_UNKNOWN_STATE + 0, // 1 -> FAST_INIT_CLOSE_STATE + 1, // 2 -> FAST_INIT_FAR_STATE + 2, // 3 -> FAST_INIT_LOST_STATE +}; + +const std::string& FastInitState_Name( + FastInitState value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + FastInitState_entries, + FastInitState_entries_by_number, + 4, FastInitState_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + FastInitState_entries, + FastInitState_entries_by_number, + 4, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + FastInitState_strings[idx].get(); +} +bool FastInitState_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FastInitState* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + FastInitState_entries, 4, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} +bool FastInitType_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed FastInitType_strings[3] = {}; + +static const char FastInitType_names[] = + "FAST_INIT_NOTIFY_TYPE" + "FAST_INIT_SILENT_TYPE" + "FAST_INIT_UNKNOWN_TYPE"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry FastInitType_entries[] = { + { {FastInitType_names + 0, 21}, 1 }, + { {FastInitType_names + 21, 21}, 2 }, + { {FastInitType_names + 42, 22}, 0 }, +}; + +static const int FastInitType_entries_by_number[] = { + 2, // 0 -> FAST_INIT_UNKNOWN_TYPE + 0, // 1 -> FAST_INIT_NOTIFY_TYPE + 1, // 2 -> FAST_INIT_SILENT_TYPE +}; + +const std::string& FastInitType_Name( + FastInitType value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + FastInitType_entries, + FastInitType_entries_by_number, + 3, FastInitType_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + FastInitType_entries, + FastInitType_entries_by_number, + 3, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + FastInitType_strings[idx].get(); +} +bool FastInitType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FastInitType* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + FastInitType_entries, 3, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} +bool DesktopNotification_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed DesktopNotification_strings[6] = {}; + +static const char DesktopNotification_names[] = + "DESKTOP_NOTIFICATION_ACCEPT" + "DESKTOP_NOTIFICATION_CONNECTING" + "DESKTOP_NOTIFICATION_ERROR" + "DESKTOP_NOTIFICATION_PROGRESS" + "DESKTOP_NOTIFICATION_RECEIVED" + "DESKTOP_NOTIFICATION_UNKNOWN"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry DesktopNotification_entries[] = { + { {DesktopNotification_names + 0, 27}, 3 }, + { {DesktopNotification_names + 27, 31}, 1 }, + { {DesktopNotification_names + 58, 26}, 5 }, + { {DesktopNotification_names + 84, 29}, 2 }, + { {DesktopNotification_names + 113, 29}, 4 }, + { {DesktopNotification_names + 142, 28}, 0 }, +}; + +static const int DesktopNotification_entries_by_number[] = { + 5, // 0 -> DESKTOP_NOTIFICATION_UNKNOWN + 1, // 1 -> DESKTOP_NOTIFICATION_CONNECTING + 3, // 2 -> DESKTOP_NOTIFICATION_PROGRESS + 0, // 3 -> DESKTOP_NOTIFICATION_ACCEPT + 4, // 4 -> DESKTOP_NOTIFICATION_RECEIVED + 2, // 5 -> DESKTOP_NOTIFICATION_ERROR +}; + +const std::string& DesktopNotification_Name( + DesktopNotification value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + DesktopNotification_entries, + DesktopNotification_entries_by_number, + 6, DesktopNotification_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + DesktopNotification_entries, + DesktopNotification_entries_by_number, + 6, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + DesktopNotification_strings[idx].get(); +} +bool DesktopNotification_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, DesktopNotification* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + DesktopNotification_entries, 6, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} +bool DesktopTransferEventType_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed DesktopTransferEventType_strings[10] = {}; + +static const char DesktopTransferEventType_names[] = + "DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_ACCEPT" + "DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_ERROR" + "DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_PROGRESS" + "DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_RECEIVED" + "DESKTOP_TRANSFER_EVENT_SEND_TYPE_ERROR" + "DESKTOP_TRANSFER_EVENT_SEND_TYPE_PROGRESS" + "DESKTOP_TRANSFER_EVENT_SEND_TYPE_SELECT_A_DEVICE" + "DESKTOP_TRANSFER_EVENT_SEND_TYPE_SENT" + "DESKTOP_TRANSFER_EVENT_SEND_TYPE_START" + "DESKTOP_TRANSFER_EVENT_TYPE_UNKNOWN"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry DesktopTransferEventType_entries[] = { + { {DesktopTransferEventType_names + 0, 42}, 1 }, + { {DesktopTransferEventType_names + 42, 41}, 4 }, + { {DesktopTransferEventType_names + 83, 44}, 2 }, + { {DesktopTransferEventType_names + 127, 44}, 3 }, + { {DesktopTransferEventType_names + 171, 38}, 9 }, + { {DesktopTransferEventType_names + 209, 41}, 7 }, + { {DesktopTransferEventType_names + 250, 48}, 6 }, + { {DesktopTransferEventType_names + 298, 37}, 8 }, + { {DesktopTransferEventType_names + 335, 38}, 5 }, + { {DesktopTransferEventType_names + 373, 35}, 0 }, +}; + +static const int DesktopTransferEventType_entries_by_number[] = { + 9, // 0 -> DESKTOP_TRANSFER_EVENT_TYPE_UNKNOWN + 0, // 1 -> DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_ACCEPT + 2, // 2 -> DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_PROGRESS + 3, // 3 -> DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_RECEIVED + 1, // 4 -> DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_ERROR + 8, // 5 -> DESKTOP_TRANSFER_EVENT_SEND_TYPE_START + 6, // 6 -> DESKTOP_TRANSFER_EVENT_SEND_TYPE_SELECT_A_DEVICE + 5, // 7 -> DESKTOP_TRANSFER_EVENT_SEND_TYPE_PROGRESS + 7, // 8 -> DESKTOP_TRANSFER_EVENT_SEND_TYPE_SENT + 4, // 9 -> DESKTOP_TRANSFER_EVENT_SEND_TYPE_ERROR +}; + +const std::string& DesktopTransferEventType_Name( + DesktopTransferEventType value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + DesktopTransferEventType_entries, + DesktopTransferEventType_entries_by_number, + 10, DesktopTransferEventType_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + DesktopTransferEventType_entries, + DesktopTransferEventType_entries_by_number, + 10, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + DesktopTransferEventType_strings[idx].get(); +} +bool DesktopTransferEventType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, DesktopTransferEventType* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + DesktopTransferEventType_entries, 10, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} +bool DecryptCertificateFailureStatus_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + return true; + default: + return false; + } +} + +static ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed DecryptCertificateFailureStatus_strings[7] = {}; + +static const char DecryptCertificateFailureStatus_names[] = + "DECRYPT_CERT_BAD_PADDING_FAILURE" + "DECRYPT_CERT_ILLEGAL_BLOCK_SIZE_FAILURE" + "DECRYPT_CERT_INVALID_ALGORITHM_PARAMETER_FAILURE" + "DECRYPT_CERT_INVALID_KEY_FAILURE" + "DECRYPT_CERT_NO_SUCH_ALGORITHM_FAILURE" + "DECRYPT_CERT_NO_SUCH_PADDING_FAILURE" + "DECRYPT_CERT_UNKNOWN_FAILURE"; + +static const ::PROTOBUF_NAMESPACE_ID::internal::EnumEntry DecryptCertificateFailureStatus_entries[] = { + { {DecryptCertificateFailureStatus_names + 0, 32}, 6 }, + { {DecryptCertificateFailureStatus_names + 32, 39}, 5 }, + { {DecryptCertificateFailureStatus_names + 71, 48}, 4 }, + { {DecryptCertificateFailureStatus_names + 119, 32}, 3 }, + { {DecryptCertificateFailureStatus_names + 151, 38}, 1 }, + { {DecryptCertificateFailureStatus_names + 189, 36}, 2 }, + { {DecryptCertificateFailureStatus_names + 225, 28}, 0 }, +}; + +static const int DecryptCertificateFailureStatus_entries_by_number[] = { + 6, // 0 -> DECRYPT_CERT_UNKNOWN_FAILURE + 4, // 1 -> DECRYPT_CERT_NO_SUCH_ALGORITHM_FAILURE + 5, // 2 -> DECRYPT_CERT_NO_SUCH_PADDING_FAILURE + 3, // 3 -> DECRYPT_CERT_INVALID_KEY_FAILURE + 2, // 4 -> DECRYPT_CERT_INVALID_ALGORITHM_PARAMETER_FAILURE + 1, // 5 -> DECRYPT_CERT_ILLEGAL_BLOCK_SIZE_FAILURE + 0, // 6 -> DECRYPT_CERT_BAD_PADDING_FAILURE +}; + +const std::string& DecryptCertificateFailureStatus_Name( + DecryptCertificateFailureStatus value) { + static const bool dummy = + ::PROTOBUF_NAMESPACE_ID::internal::InitializeEnumStrings( + DecryptCertificateFailureStatus_entries, + DecryptCertificateFailureStatus_entries_by_number, + 7, DecryptCertificateFailureStatus_strings); + (void) dummy; + int idx = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumName( + DecryptCertificateFailureStatus_entries, + DecryptCertificateFailureStatus_entries_by_number, + 7, value); + return idx == -1 ? ::PROTOBUF_NAMESPACE_ID::internal::GetEmptyString() : + DecryptCertificateFailureStatus_strings[idx].get(); +} +bool DecryptCertificateFailureStatus_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, DecryptCertificateFailureStatus* value) { + int int_value; + bool success = ::PROTOBUF_NAMESPACE_ID::internal::LookUpEnumValue( + DecryptCertificateFailureStatus_entries, 7, name, &int_value); + if (success) { + *value = static_cast(int_value); + } + return success; +} // @@protoc_insertion_point(namespace_scope) } // namespace sharing diff --git a/compiled_proto/proto/sharing_enums.pb.h b/compiled_proto/proto/sharing_enums.pb.h index 21e0a4b7..6bf8b7c6 100644 --- a/compiled_proto/proto/sharing_enums.pb.h +++ b/compiled_proto/proto/sharing_enums.pb.h @@ -117,11 +117,16 @@ enum EventType : int { DEFAULT_OPT_IN = 56, SETUP_WIZARD = 57, TAP_QR_CODE = 58, - QR_CODE_LINK_SHOWN = 59 + QR_CODE_LINK_SHOWN = 59, + PARSING_FAILED_ENDPOINT_ID = 60, + FAST_INIT_DISCOVER_DEVICE = 61, + SEND_DESKTOP_NOTIFICATION = 62, + SET_ACCOUNT = 63, + DECRYPT_CERTIFICATE_FAILURE = 64 }; bool EventType_IsValid(int value); constexpr EventType EventType_MIN = UNKNOWN_EVENT_TYPE; -constexpr EventType EventType_MAX = QR_CODE_LINK_SHOWN; +constexpr EventType EventType_MAX = DECRYPT_CERTIFICATE_FAILURE; constexpr int EventType_ARRAYSIZE = EventType_MAX + 1; const std::string& EventType_Name(EventType value); @@ -258,11 +263,15 @@ enum AttachmentTransmissionStatus : int { FAILED_NULL_CONNECTION = 14, FAILED_NO_PAYLOAD = 15, FAILED_WRITE_INTRODUCTION = 16, - FAILED_UNKNOWN_REMOTE_RESPONSE = 17 + FAILED_UNKNOWN_REMOTE_RESPONSE = 17, + FAILED_NULL_CONNECTION_INIT_OUTGOING = 18, + FAILED_NULL_CONNECTION_DISCONNECTED = 19, + FAILED_NULL_CONNECTION_LOST_CONNECTIVITY = 20, + FAILED_NULL_CONNECTION_FAILURE = 21 }; bool AttachmentTransmissionStatus_IsValid(int value); constexpr AttachmentTransmissionStatus AttachmentTransmissionStatus_MIN = UNKNOWN_ATTACHMENT_TRANSMISSION_STATUS; -constexpr AttachmentTransmissionStatus AttachmentTransmissionStatus_MAX = FAILED_UNKNOWN_REMOTE_RESPONSE; +constexpr AttachmentTransmissionStatus AttachmentTransmissionStatus_MAX = FAILED_NULL_CONNECTION_FAILURE; constexpr int AttachmentTransmissionStatus_ARRAYSIZE = AttachmentTransmissionStatus_MAX + 1; const std::string& AttachmentTransmissionStatus_Name(AttachmentTransmissionStatus value); @@ -275,6 +284,42 @@ inline const std::string& AttachmentTransmissionStatus_Name(T enum_t_value) { } bool AttachmentTransmissionStatus_Parse( ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, AttachmentTransmissionStatus* value); +enum ConnectionLayerStatus : int { + CONNECTION_LAYER_STATUS_UNKNOWN = 0, + CONNECTION_LAYER_STATUS_SUCCESS = 1, + CONNECTION_LAYER_STATUS_ERROR = 2, + CONNECTION_LAYER_STATUS_OUT_OF_ORDER_API_CALL = 3, + CONNECTION_LAYER_STATUS_ALREADY_HAVE_ACTIVE_STRATEGY = 4, + CONNECTION_LAYER_STATUS_ALREADY_ADVERTISING = 5, + CONNECTION_LAYER_STATUS_ALREADY_DISCOVERING = 6, + CONNECTION_LAYER_STATUS_ALREADY_LISTENING = 7, + CONNECTION_LAYER_STATUS_END_POINT_IO_ERROR = 8, + CONNECTION_LAYER_STATUS_END_POINT_UNKNOWN = 9, + CONNECTION_LAYER_STATUS_CONNECTION_REJECTED = 10, + CONNECTION_LAYER_STATUS_ALREADY_CONNECTED_TO_END_POINT = 11, + CONNECTION_LAYER_STATUS_NOT_CONNECTED_TO_END_POINT = 12, + CONNECTION_LAYER_STATUS_BLUETOOTH_ERROR = 13, + CONNECTION_LAYER_STATUS_BLE_ERROR = 14, + CONNECTION_LAYER_STATUS_WIFI_LAN_ERROR = 15, + CONNECTION_LAYER_STATUS_PAYLOAD_UNKNOWN = 16, + CONNECTION_LAYER_STATUS_RESET = 17, + CONNECTION_LAYER_STATUS_TIMEOUT = 18 +}; +bool ConnectionLayerStatus_IsValid(int value); +constexpr ConnectionLayerStatus ConnectionLayerStatus_MIN = CONNECTION_LAYER_STATUS_UNKNOWN; +constexpr ConnectionLayerStatus ConnectionLayerStatus_MAX = CONNECTION_LAYER_STATUS_TIMEOUT; +constexpr int ConnectionLayerStatus_ARRAYSIZE = ConnectionLayerStatus_MAX + 1; + +const std::string& ConnectionLayerStatus_Name(ConnectionLayerStatus value); +template +inline const std::string& ConnectionLayerStatus_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function ConnectionLayerStatus_Name."); + return ConnectionLayerStatus_Name(static_cast(enum_t_value)); +} +bool ConnectionLayerStatus_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ConnectionLayerStatus* value); enum ProcessReceivedAttachmentsStatus : int { PROCESSING_STATUS_UNKNOWN = 0, PROCESSING_STATUS_COMPLETE_PROCESSING_ATTACHMENTS = 1, @@ -483,6 +528,59 @@ inline const std::string& ServerResponseState_Name(T enum_t_value) { } bool ServerResponseState_Parse( ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ServerResponseState* value); +enum SyncPurpose : int { + SYNC_PURPOSE_UNKNOWN = 0, + SYNC_PURPOSE_ON_DEMAND_SYNC = 1, + SYNC_PURPOSE_CHIME_NOTIFICATION = 2, + SYNC_PURPOSE_DAILY_SYNC = 3, + SYNC_PURPOSE_OPT_IN_FIRST_SYNC = 4, + SYNC_PURPOSE_CHECK_DEFAULT_OPT_IN = 5, + SYNC_PURPOSE_NEARBY_SHARE_ENABLED = 6, + SYNC_PURPOSE_SYNC_AT_FAST_INIT = 7, + SYNC_PURPOSE_SYNC_AT_DISCOVERY = 8, + SYNC_PURPOSE_SYNC_AT_LOAD_PRIVATE_CERTIFICATE = 9, + SYNC_PURPOSE_SYNC_AT_ADVERTISEMENT = 10, + SYNC_PURPOSE_CONTACT_LIST_CHANGE = 11, + SYNC_PURPOSE_SHOW_C11N_VIEW = 12, + SYNC_PURPOSE_REGULAR_CHECK_CONTACT_REACHABILITY = 13, + SYNC_PURPOSE_VISIBILITY_SELECTED_CONTACT_CHANGE = 14, + SYNC_PURPOSE_ACCOUNT_CHANGE = 15 +}; +bool SyncPurpose_IsValid(int value); +constexpr SyncPurpose SyncPurpose_MIN = SYNC_PURPOSE_UNKNOWN; +constexpr SyncPurpose SyncPurpose_MAX = SYNC_PURPOSE_ACCOUNT_CHANGE; +constexpr int SyncPurpose_ARRAYSIZE = SyncPurpose_MAX + 1; + +const std::string& SyncPurpose_Name(SyncPurpose value); +template +inline const std::string& SyncPurpose_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function SyncPurpose_Name."); + return SyncPurpose_Name(static_cast(enum_t_value)); +} +bool SyncPurpose_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, SyncPurpose* value); +enum ClientRole : int { + CLIENT_ROLE_UNKNOWN = 0, + CLIENT_ROLE_SENDER = 1, + CLIENT_ROLE_RECEIVER = 2 +}; +bool ClientRole_IsValid(int value); +constexpr ClientRole ClientRole_MIN = CLIENT_ROLE_UNKNOWN; +constexpr ClientRole ClientRole_MAX = CLIENT_ROLE_RECEIVER; +constexpr int ClientRole_ARRAYSIZE = ClientRole_MAX + 1; + +const std::string& ClientRole_Name(ClientRole value); +template +inline const std::string& ClientRole_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function ClientRole_Name."); + return ClientRole_Name(static_cast(enum_t_value)); +} +bool ClientRole_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ClientRole* value); enum ScanType : int { UNKNOWN_SCAN_TYPE = 0, FOREGROUND_SCAN = 1, @@ -505,6 +603,26 @@ inline const std::string& ScanType_Name(T enum_t_value) { } bool ScanType_Parse( ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ScanType* value); +enum ParsingFailedType : int { + FAILED_UNKNOWN_TYPE = 0, + FAILED_PARSE_ADVERTISEMENT = 1, + FAILED_CONVERT_SHARE_TARGET = 2 +}; +bool ParsingFailedType_IsValid(int value); +constexpr ParsingFailedType ParsingFailedType_MIN = FAILED_UNKNOWN_TYPE; +constexpr ParsingFailedType ParsingFailedType_MAX = FAILED_CONVERT_SHARE_TARGET; +constexpr int ParsingFailedType_ARRAYSIZE = ParsingFailedType_MAX + 1; + +const std::string& ParsingFailedType_Name(ParsingFailedType value); +template +inline const std::string& ParsingFailedType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function ParsingFailedType_Name."); + return ParsingFailedType_Name(static_cast(enum_t_value)); +} +bool ParsingFailedType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ParsingFailedType* value); enum AdvertisingMode : int { UNKNOWN_ADVERTISING_MODE = 0, SCREEN_OFF_ADVERTISING_MODE = 1, @@ -838,6 +956,121 @@ inline const std::string& PreferencesActionStatus_Name(T enum_t_value) { } bool PreferencesActionStatus_Parse( ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, PreferencesActionStatus* value); +enum FastInitState : int { + FAST_INIT_UNKNOWN_STATE = 0, + FAST_INIT_CLOSE_STATE = 1, + FAST_INIT_FAR_STATE = 2, + FAST_INIT_LOST_STATE = 3 +}; +bool FastInitState_IsValid(int value); +constexpr FastInitState FastInitState_MIN = FAST_INIT_UNKNOWN_STATE; +constexpr FastInitState FastInitState_MAX = FAST_INIT_LOST_STATE; +constexpr int FastInitState_ARRAYSIZE = FastInitState_MAX + 1; + +const std::string& FastInitState_Name(FastInitState value); +template +inline const std::string& FastInitState_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function FastInitState_Name."); + return FastInitState_Name(static_cast(enum_t_value)); +} +bool FastInitState_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FastInitState* value); +enum FastInitType : int { + FAST_INIT_UNKNOWN_TYPE = 0, + FAST_INIT_NOTIFY_TYPE = 1, + FAST_INIT_SILENT_TYPE = 2 +}; +bool FastInitType_IsValid(int value); +constexpr FastInitType FastInitType_MIN = FAST_INIT_UNKNOWN_TYPE; +constexpr FastInitType FastInitType_MAX = FAST_INIT_SILENT_TYPE; +constexpr int FastInitType_ARRAYSIZE = FastInitType_MAX + 1; + +const std::string& FastInitType_Name(FastInitType value); +template +inline const std::string& FastInitType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function FastInitType_Name."); + return FastInitType_Name(static_cast(enum_t_value)); +} +bool FastInitType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, FastInitType* value); +enum DesktopNotification : int { + DESKTOP_NOTIFICATION_UNKNOWN = 0, + DESKTOP_NOTIFICATION_CONNECTING = 1, + DESKTOP_NOTIFICATION_PROGRESS = 2, + DESKTOP_NOTIFICATION_ACCEPT = 3, + DESKTOP_NOTIFICATION_RECEIVED = 4, + DESKTOP_NOTIFICATION_ERROR = 5 +}; +bool DesktopNotification_IsValid(int value); +constexpr DesktopNotification DesktopNotification_MIN = DESKTOP_NOTIFICATION_UNKNOWN; +constexpr DesktopNotification DesktopNotification_MAX = DESKTOP_NOTIFICATION_ERROR; +constexpr int DesktopNotification_ARRAYSIZE = DesktopNotification_MAX + 1; + +const std::string& DesktopNotification_Name(DesktopNotification value); +template +inline const std::string& DesktopNotification_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function DesktopNotification_Name."); + return DesktopNotification_Name(static_cast(enum_t_value)); +} +bool DesktopNotification_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, DesktopNotification* value); +enum DesktopTransferEventType : int { + DESKTOP_TRANSFER_EVENT_TYPE_UNKNOWN = 0, + DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_ACCEPT = 1, + DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_PROGRESS = 2, + DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_RECEIVED = 3, + DESKTOP_TRANSFER_EVENT_RECEIVE_TYPE_ERROR = 4, + DESKTOP_TRANSFER_EVENT_SEND_TYPE_START = 5, + DESKTOP_TRANSFER_EVENT_SEND_TYPE_SELECT_A_DEVICE = 6, + DESKTOP_TRANSFER_EVENT_SEND_TYPE_PROGRESS = 7, + DESKTOP_TRANSFER_EVENT_SEND_TYPE_SENT = 8, + DESKTOP_TRANSFER_EVENT_SEND_TYPE_ERROR = 9 +}; +bool DesktopTransferEventType_IsValid(int value); +constexpr DesktopTransferEventType DesktopTransferEventType_MIN = DESKTOP_TRANSFER_EVENT_TYPE_UNKNOWN; +constexpr DesktopTransferEventType DesktopTransferEventType_MAX = DESKTOP_TRANSFER_EVENT_SEND_TYPE_ERROR; +constexpr int DesktopTransferEventType_ARRAYSIZE = DesktopTransferEventType_MAX + 1; + +const std::string& DesktopTransferEventType_Name(DesktopTransferEventType value); +template +inline const std::string& DesktopTransferEventType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function DesktopTransferEventType_Name."); + return DesktopTransferEventType_Name(static_cast(enum_t_value)); +} +bool DesktopTransferEventType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, DesktopTransferEventType* value); +enum DecryptCertificateFailureStatus : int { + DECRYPT_CERT_UNKNOWN_FAILURE = 0, + DECRYPT_CERT_NO_SUCH_ALGORITHM_FAILURE = 1, + DECRYPT_CERT_NO_SUCH_PADDING_FAILURE = 2, + DECRYPT_CERT_INVALID_KEY_FAILURE = 3, + DECRYPT_CERT_INVALID_ALGORITHM_PARAMETER_FAILURE = 4, + DECRYPT_CERT_ILLEGAL_BLOCK_SIZE_FAILURE = 5, + DECRYPT_CERT_BAD_PADDING_FAILURE = 6 +}; +bool DecryptCertificateFailureStatus_IsValid(int value); +constexpr DecryptCertificateFailureStatus DecryptCertificateFailureStatus_MIN = DECRYPT_CERT_UNKNOWN_FAILURE; +constexpr DecryptCertificateFailureStatus DecryptCertificateFailureStatus_MAX = DECRYPT_CERT_BAD_PADDING_FAILURE; +constexpr int DecryptCertificateFailureStatus_ARRAYSIZE = DecryptCertificateFailureStatus_MAX + 1; + +const std::string& DecryptCertificateFailureStatus_Name(DecryptCertificateFailureStatus value); +template +inline const std::string& DecryptCertificateFailureStatus_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function DecryptCertificateFailureStatus_Name."); + return DecryptCertificateFailureStatus_Name(static_cast(enum_t_value)); +} +bool DecryptCertificateFailureStatus_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, DecryptCertificateFailureStatus* value); // =================================================================== @@ -870,6 +1103,7 @@ template <> struct is_proto_enum< ::location::nearby::proto::sharing::Visibility template <> struct is_proto_enum< ::location::nearby::proto::sharing::DataUsage> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::EstablishConnectionStatus> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::AttachmentTransmissionStatus> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::sharing::ConnectionLayerStatus> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::ProcessReceivedAttachmentsStatus> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::SessionStatus> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::ResponseToIntroduction> : ::std::true_type {}; @@ -879,7 +1113,10 @@ template <> struct is_proto_enum< ::location::nearby::proto::sharing::DeviceRela template <> struct is_proto_enum< ::location::nearby::proto::sharing::LogSource> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::ServerActionName> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::ServerResponseState> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::sharing::SyncPurpose> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::sharing::ClientRole> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::ScanType> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::sharing::ParsingFailedType> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::AdvertisingMode> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::ActivityName> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::ConsentType> : ::std::true_type {}; @@ -895,6 +1132,11 @@ template <> struct is_proto_enum< ::location::nearby::proto::sharing::AppCrashRe template <> struct is_proto_enum< ::location::nearby::proto::sharing::AttachmentSourceType> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::PreferencesAction> : ::std::true_type {}; template <> struct is_proto_enum< ::location::nearby::proto::sharing::PreferencesActionStatus> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::sharing::FastInitState> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::sharing::FastInitType> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::sharing::DesktopNotification> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::sharing::DesktopTransferEventType> : ::std::true_type {}; +template <> struct is_proto_enum< ::location::nearby::proto::sharing::DecryptCertificateFailureStatus> : ::std::true_type {}; PROTOBUF_NAMESPACE_CLOSE From 6074e1beba4c944a02cb20f9f2458e31d61da596 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sat, 29 Jul 2023 17:08:02 -0700 Subject: [PATCH 077/128] [fp-rs] Set up basic demo for calling Rust from Flutter --- .../rust/demo/lib/bridge_definitions.dart | 15 ++ fastpair/rust/demo/lib/bridge_generated.dart | 203 ++++++++++++++++++ fastpair/rust/demo/lib/main.dart | 151 +++---------- fastpair/rust/demo/lib/rust.dart | 18 ++ fastpair/rust/demo/test/widget_test.dart | 30 --- 5 files changed, 271 insertions(+), 146 deletions(-) create mode 100644 fastpair/rust/demo/lib/bridge_definitions.dart create mode 100644 fastpair/rust/demo/lib/bridge_generated.dart create mode 100644 fastpair/rust/demo/lib/rust.dart delete mode 100644 fastpair/rust/demo/test/widget_test.dart diff --git a/fastpair/rust/demo/lib/bridge_definitions.dart b/fastpair/rust/demo/lib/bridge_definitions.dart new file mode 100644 index 00000000..9468e02f --- /dev/null +++ b/fastpair/rust/demo/lib/bridge_definitions.dart @@ -0,0 +1,15 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// Generated by `flutter_rust_bridge`@ 1.79.0. +// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const + +import 'dart:convert'; +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:uuid/uuid.dart'; + +abstract class Rust { + Future hello({dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kHelloConstMeta; +} diff --git a/fastpair/rust/demo/lib/bridge_generated.dart b/fastpair/rust/demo/lib/bridge_generated.dart new file mode 100644 index 00000000..2c075cb7 --- /dev/null +++ b/fastpair/rust/demo/lib/bridge_generated.dart @@ -0,0 +1,203 @@ +// AUTO GENERATED FILE, DO NOT EDIT. +// Generated by `flutter_rust_bridge`@ 1.79.0. +// ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const + +import "bridge_definitions.dart"; +import 'dart:convert'; +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:uuid/uuid.dart'; + +import 'dart:convert'; +import 'dart:async'; +import 'package:meta/meta.dart'; +import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; +import 'package:uuid/uuid.dart'; + +import 'dart:ffi' as ffi; + +class RustImpl implements Rust { + final RustPlatform _platform; + factory RustImpl(ExternalLibrary dylib) => RustImpl.raw(RustPlatform(dylib)); + + /// Only valid on web/WASM platforms. + factory RustImpl.wasm(FutureOr module) => + RustImpl(module as ExternalLibrary); + RustImpl.raw(this._platform); + Future hello({dynamic hint}) { + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => _platform.inner.wire_hello(port_), + parseSuccessData: _wire2api_String, + constMeta: kHelloConstMeta, + argValues: [], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kHelloConstMeta => + const FlutterRustBridgeTaskConstMeta( + debugName: "hello", + argNames: [], + ); + + void dispose() { + _platform.dispose(); + } +// Section: wire2api + + String _wire2api_String(dynamic raw) { + return raw as String; + } + + int _wire2api_u8(dynamic raw) { + return raw as int; + } + + Uint8List _wire2api_uint_8_list(dynamic raw) { + return raw as Uint8List; + } +} + +// Section: api2wire + +// Section: finalizer + +class RustPlatform extends FlutterRustBridgeBase { + RustPlatform(ffi.DynamicLibrary dylib) : super(RustWire(dylib)); + +// Section: api2wire + +// Section: finalizer + +// Section: api_fill_to_wire +} + +// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint + +/// generated by flutter_rust_bridge +class RustWire implements FlutterRustBridgeWireBase { + @internal + late final dartApi = DartApiDl(init_frb_dart_api_dl); + + /// Holds the symbol lookup function. + final ffi.Pointer Function(String symbolName) + _lookup; + + /// The symbols are looked up in [dynamicLibrary]. + RustWire(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; + + /// The symbols are looked up with [lookup]. + RustWire.fromLookup( + ffi.Pointer Function(String symbolName) + lookup) + : _lookup = lookup; + + void store_dart_post_cobject( + DartPostCObjectFnType ptr, + ) { + return _store_dart_post_cobject( + ptr, + ); + } + + late final _store_dart_post_cobjectPtr = + _lookup>( + 'store_dart_post_cobject'); + late final _store_dart_post_cobject = _store_dart_post_cobjectPtr + .asFunction(); + + Object get_dart_object( + int ptr, + ) { + return _get_dart_object( + ptr, + ); + } + + late final _get_dart_objectPtr = + _lookup>( + 'get_dart_object'); + late final _get_dart_object = + _get_dart_objectPtr.asFunction(); + + void drop_dart_object( + int ptr, + ) { + return _drop_dart_object( + ptr, + ); + } + + late final _drop_dart_objectPtr = + _lookup>( + 'drop_dart_object'); + late final _drop_dart_object = + _drop_dart_objectPtr.asFunction(); + + int new_dart_opaque( + Object handle, + ) { + return _new_dart_opaque( + handle, + ); + } + + late final _new_dart_opaquePtr = + _lookup>( + 'new_dart_opaque'); + late final _new_dart_opaque = + _new_dart_opaquePtr.asFunction(); + + int init_frb_dart_api_dl( + ffi.Pointer obj, + ) { + return _init_frb_dart_api_dl( + obj, + ); + } + + late final _init_frb_dart_api_dlPtr = + _lookup)>>( + 'init_frb_dart_api_dl'); + late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr + .asFunction)>(); + + void wire_hello( + int port_, + ) { + return _wire_hello( + port_, + ); + } + + late final _wire_helloPtr = + _lookup>('wire_hello'); + late final _wire_hello = _wire_helloPtr.asFunction(); + + void free_WireSyncReturn( + WireSyncReturn ptr, + ) { + return _free_WireSyncReturn( + ptr, + ); + } + + late final _free_WireSyncReturnPtr = + _lookup>( + 'free_WireSyncReturn'); + late final _free_WireSyncReturn = + _free_WireSyncReturnPtr.asFunction(); +} + +final class _Dart_Handle extends ffi.Opaque {} + +typedef DartPostCObjectFnType = ffi.Pointer< + ffi.NativeFunction< + ffi.Bool Function(DartPort port_id, ffi.Pointer message)>>; +typedef DartPort = ffi.Int64; diff --git a/fastpair/rust/demo/lib/main.dart b/fastpair/rust/demo/lib/main.dart index dda55548..45f354dc 100644 --- a/fastpair/rust/demo/lib/main.dart +++ b/fastpair/rust/demo/lib/main.dart @@ -1,125 +1,44 @@ import 'package:flutter/material.dart'; +import 'package:demo/rust.dart'; void main() { - runApp(const MyApp()); + runApp(const FastPairApp()); } -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a blue toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - useMaterial3: true, - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; +class FastPairApp extends StatelessWidget { + const FastPairApp({super.key}); @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'You have pushed the button this many times:', - ), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], + Widget build(BuildContext context) => MaterialApp( + title: 'Fast Pair', + theme: ThemeData( + primarySwatch: Colors.blue, ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } + home: const HomePage(), + ); +} + +class HomePage extends StatelessWidget { + const HomePage({super.key}); + + @override + Widget build(BuildContext context) => Scaffold( + appBar: AppBar( + title: const Text("Fast Pair"), + ), + body: Center( + child: FutureBuilder( + // All Rust functions are called as Future's + future: api.hello(), // The Rust function we are calling. + builder: (context, data) { + if (data.hasData) { + return Text(data.data!); // The string to display + } + return const Center( + child: CircularProgressIndicator(), + ); + }, + ), + ), + ); } diff --git a/fastpair/rust/demo/lib/rust.dart b/fastpair/rust/demo/lib/rust.dart new file mode 100644 index 00000000..9a3a86a9 --- /dev/null +++ b/fastpair/rust/demo/lib/rust.dart @@ -0,0 +1,18 @@ +// This file initializes the dynamic library and connects it with the stub +// generated by flutter_rust_bridge_codegen. + +import 'dart:ffi'; + +import 'dart:io' as io; + +import 'package:demo/bridge_generated.dart'; + +const _base = 'rust'; + +// On MacOS, the dynamic library is not bundled with the binary, +// but rather directly **linked** against the binary. +final _dylib = io.Platform.isWindows ? '$_base.dll' : 'lib$_base.so'; + +final api = RustImpl(io.Platform.isIOS || io.Platform.isMacOS + ? DynamicLibrary.executable() + : DynamicLibrary.open(_dylib)); diff --git a/fastpair/rust/demo/test/widget_test.dart b/fastpair/rust/demo/test/widget_test.dart deleted file mode 100644 index e25abebe..00000000 --- a/fastpair/rust/demo/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:demo/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} From 2c8809c1e10a63c047f338d5e5bebb6da0cc7e1f Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sat, 29 Jul 2023 18:02:53 -0700 Subject: [PATCH 078/128] [fp-rs] Converted bluetooth module into standalone crate, with fastpair UI provided as an example. --- fastpair/rust/bluetooth/Cargo.toml | 7 ++- .../{src/main.rs => examples/fastpair_ui.rs} | 8 +-- .../src/{bluetooth => }/api/adapter.rs | 4 +- .../src/{bluetooth => }/api/device.rs | 2 +- .../bluetooth/src/{bluetooth => }/api/mod.rs | 0 fastpair/rust/bluetooth/src/bluetooth/mod.rs | 56 ------------------- .../src/{bluetooth => }/common/address.rs | 2 +- .../{bluetooth => }/common/advertisement.rs | 0 .../src/{bluetooth => }/common/error.rs | 0 .../src/{bluetooth => }/common/mod.rs | 0 fastpair/rust/bluetooth/src/lib.rs | 40 ++++++++++++- .../{bluetooth => }/unsupported/adapter.rs | 4 +- .../src/{bluetooth => }/unsupported/device.rs | 2 +- .../src/{bluetooth => }/unsupported/mod.rs | 0 .../src/{bluetooth => }/windows/adapter.rs | 2 +- .../src/{bluetooth => }/windows/address.rs | 2 +- .../{bluetooth => }/windows/advertisement.rs | 2 +- .../src/{bluetooth => }/windows/device.rs | 2 +- .../src/{bluetooth => }/windows/error.rs | 2 +- .../src/{bluetooth => }/windows/mod.rs | 0 .../rust/bluetooth/tests/integration_test.rs | 2 +- 21 files changed, 58 insertions(+), 79 deletions(-) rename fastpair/rust/bluetooth/{src/main.rs => examples/fastpair_ui.rs} (96%) rename fastpair/rust/bluetooth/src/{bluetooth => }/api/adapter.rs (93%) rename fastpair/rust/bluetooth/src/{bluetooth => }/api/device.rs (98%) rename fastpair/rust/bluetooth/src/{bluetooth => }/api/mod.rs (100%) delete mode 100644 fastpair/rust/bluetooth/src/bluetooth/mod.rs rename fastpair/rust/bluetooth/src/{bluetooth => }/common/address.rs (98%) rename fastpair/rust/bluetooth/src/{bluetooth => }/common/advertisement.rs (100%) rename fastpair/rust/bluetooth/src/{bluetooth => }/common/error.rs (100%) rename fastpair/rust/bluetooth/src/{bluetooth => }/common/mod.rs (100%) rename fastpair/rust/bluetooth/src/{bluetooth => }/unsupported/adapter.rs (93%) rename fastpair/rust/bluetooth/src/{bluetooth => }/unsupported/device.rs (98%) rename fastpair/rust/bluetooth/src/{bluetooth => }/unsupported/mod.rs (100%) rename fastpair/rust/bluetooth/src/{bluetooth => }/windows/adapter.rs (99%) rename fastpair/rust/bluetooth/src/{bluetooth => }/windows/address.rs (97%) rename fastpair/rust/bluetooth/src/{bluetooth => }/windows/advertisement.rs (99%) rename fastpair/rust/bluetooth/src/{bluetooth => }/windows/device.rs (98%) rename fastpair/rust/bluetooth/src/{bluetooth => }/windows/error.rs (98%) rename fastpair/rust/bluetooth/src/{bluetooth => }/windows/mod.rs (100%) diff --git a/fastpair/rust/bluetooth/Cargo.toml b/fastpair/rust/bluetooth/Cargo.toml index 02b48ebc..0c666219 100644 --- a/fastpair/rust/bluetooth/Cargo.toml +++ b/fastpair/rust/bluetooth/Cargo.toml @@ -13,19 +13,22 @@ # limitations under the License. [package] -name = "fastpair" +name = "bluetooth" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -futures = { version = "0.3", features = ["executor"] } +futures = { version = "0.3" } tracing = "0.1.37" cfg-if = "1.0.0" async-trait = "0.1" thiserror = "1.0.43" +[dev-dependencies] +futures = { version = "0.3", features = ["executor"] } + [target.'cfg(windows)'.dependencies] windows = { version = "0.48", features = [ "Devices_Bluetooth", diff --git a/fastpair/rust/bluetooth/src/main.rs b/fastpair/rust/bluetooth/examples/fastpair_ui.rs similarity index 96% rename from fastpair/rust/bluetooth/src/main.rs rename to fastpair/rust/bluetooth/examples/fastpair_ui.rs index 19d77c00..920ace4c 100644 --- a/fastpair/rust/bluetooth/src/main.rs +++ b/fastpair/rust/bluetooth/examples/fastpair_ui.rs @@ -25,11 +25,11 @@ use futures::{ lock::Mutex, }; -mod bluetooth; +extern crate bluetooth; -use crate::bluetooth::{ - BleAdapter, BleDataTypeId, BleDevice, ClassicAddress, ClassicDevice, - Platform, +use bluetooth::{ + api::{BleAdapter, BleDevice, ClassicDevice}, + BleDataTypeId, ClassicAddress, Platform, }; async fn get_user_input( diff --git a/fastpair/rust/bluetooth/src/bluetooth/api/adapter.rs b/fastpair/rust/bluetooth/src/api/adapter.rs similarity index 93% rename from fastpair/rust/bluetooth/src/bluetooth/api/adapter.rs rename to fastpair/rust/bluetooth/src/api/adapter.rs index 39ab9310..de9bc43f 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/api/adapter.rs +++ b/fastpair/rust/bluetooth/src/api/adapter.rs @@ -14,9 +14,7 @@ use async_trait::async_trait; -use crate::bluetooth::common::{ - BleAdvertisement, BleDataTypeId, BluetoothError, -}; +use crate::common::{BleAdvertisement, BleDataTypeId, BluetoothError}; /// Concrete types implementing this trait are Bluetooth Central devices. /// They provide methods for retrieving nearby connections and device info. diff --git a/fastpair/rust/bluetooth/src/bluetooth/api/device.rs b/fastpair/rust/bluetooth/src/api/device.rs similarity index 98% rename from fastpair/rust/bluetooth/src/bluetooth/api/device.rs rename to fastpair/rust/bluetooth/src/api/device.rs index 97cba1d8..df43ec72 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/api/device.rs +++ b/fastpair/rust/bluetooth/src/api/device.rs @@ -14,7 +14,7 @@ use async_trait::async_trait; -use crate::bluetooth::common::{ +use crate::common::{ BleAddress, BluetoothError, ClassicAddress, PairingResult, }; diff --git a/fastpair/rust/bluetooth/src/bluetooth/api/mod.rs b/fastpair/rust/bluetooth/src/api/mod.rs similarity index 100% rename from fastpair/rust/bluetooth/src/bluetooth/api/mod.rs rename to fastpair/rust/bluetooth/src/api/mod.rs diff --git a/fastpair/rust/bluetooth/src/bluetooth/mod.rs b/fastpair/rust/bluetooth/src/bluetooth/mod.rs deleted file mode 100644 index 0acdb250..00000000 --- a/fastpair/rust/bluetooth/src/bluetooth/mod.rs +++ /dev/null @@ -1,56 +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. - -// Split into separate crate once demo is finished, providing custom error types -// instead of using anyhow. -// b/290070686 - -pub mod api; -pub mod common; - -pub use api::{BleAdapter, BleDevice, ClassicDevice}; -pub use common::{ - BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError, ClassicAddress, -}; - -cfg_if::cfg_if! { - if #[cfg(windows)] { - mod windows; - use self::windows as platform; - } else { - mod unsupported; - use unsupported as platform; - } -} - -pub struct Platform; - -impl Platform { - pub async fn default_adapter( - ) -> Result { - platform::BleAdapter::default().await - } - - pub async fn new_ble_device( - addr: BleAddress, - ) -> Result { - platform::BleDevice::new(addr).await - } - - pub async fn new_classic_device( - addr: ClassicAddress, - ) -> Result { - platform::ClassicDevice::new(addr).await - } -} diff --git a/fastpair/rust/bluetooth/src/bluetooth/common/address.rs b/fastpair/rust/bluetooth/src/common/address.rs similarity index 98% rename from fastpair/rust/bluetooth/src/bluetooth/common/address.rs rename to fastpair/rust/bluetooth/src/common/address.rs index 485ff731..db0481f6 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/common/address.rs +++ b/fastpair/rust/bluetooth/src/common/address.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bluetooth::common::BluetoothError; +use super::BluetoothError; /// BLE Addresses can either be the peripheral's public MAC address, or various /// types of random addresses. diff --git a/fastpair/rust/bluetooth/src/bluetooth/common/advertisement.rs b/fastpair/rust/bluetooth/src/common/advertisement.rs similarity index 100% rename from fastpair/rust/bluetooth/src/bluetooth/common/advertisement.rs rename to fastpair/rust/bluetooth/src/common/advertisement.rs diff --git a/fastpair/rust/bluetooth/src/bluetooth/common/error.rs b/fastpair/rust/bluetooth/src/common/error.rs similarity index 100% rename from fastpair/rust/bluetooth/src/bluetooth/common/error.rs rename to fastpair/rust/bluetooth/src/common/error.rs diff --git a/fastpair/rust/bluetooth/src/bluetooth/common/mod.rs b/fastpair/rust/bluetooth/src/common/mod.rs similarity index 100% rename from fastpair/rust/bluetooth/src/bluetooth/common/mod.rs rename to fastpair/rust/bluetooth/src/common/mod.rs diff --git a/fastpair/rust/bluetooth/src/lib.rs b/fastpair/rust/bluetooth/src/lib.rs index c9a294f5..e454012b 100644 --- a/fastpair/rust/bluetooth/src/lib.rs +++ b/fastpair/rust/bluetooth/src/lib.rs @@ -12,5 +12,41 @@ // See the License for the specific language governing permissions and // limitations under the License. -/// Library file, exports modules for use in integration tests and external crates. -pub mod bluetooth; +pub mod api; +mod common; + +use api::{BleAdapter, BleDevice, ClassicDevice}; +pub use common::{ + BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError, ClassicAddress, +}; + +cfg_if::cfg_if! { + if #[cfg(windows)] { + mod windows; + use self::windows as platform; + } else { + mod unsupported; + use unsupported as platform; + } +} + +pub struct Platform; + +impl Platform { + pub async fn default_adapter( + ) -> Result { + platform::BleAdapter::default().await + } + + pub async fn new_ble_device( + addr: BleAddress, + ) -> Result { + platform::BleDevice::new(addr).await + } + + pub async fn new_classic_device( + addr: ClassicAddress, + ) -> Result { + platform::ClassicDevice::new(addr).await + } +} diff --git a/fastpair/rust/bluetooth/src/bluetooth/unsupported/adapter.rs b/fastpair/rust/bluetooth/src/unsupported/adapter.rs similarity index 93% rename from fastpair/rust/bluetooth/src/bluetooth/unsupported/adapter.rs rename to fastpair/rust/bluetooth/src/unsupported/adapter.rs index de883e3c..7ae210e1 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/unsupported/adapter.rs +++ b/fastpair/rust/bluetooth/src/unsupported/adapter.rs @@ -15,9 +15,7 @@ use async_trait::async_trait; use super::BleDevice; -use crate::bluetooth::{ - api, common::BluetoothError, BleAdvertisement, BleDataTypeId, -}; +use crate::{api, common::BluetoothError, BleAdvertisement, BleDataTypeId}; /// Concrete type implementing `Adapter`, used for unsupported devices. /// Every method should panic. diff --git a/fastpair/rust/bluetooth/src/bluetooth/unsupported/device.rs b/fastpair/rust/bluetooth/src/unsupported/device.rs similarity index 98% rename from fastpair/rust/bluetooth/src/bluetooth/unsupported/device.rs rename to fastpair/rust/bluetooth/src/unsupported/device.rs index 0ac68775..244ea06c 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/unsupported/device.rs +++ b/fastpair/rust/bluetooth/src/unsupported/device.rs @@ -14,7 +14,7 @@ use async_trait::async_trait; -use crate::bluetooth::{ +use crate::{ api, common::{BleAddress, BluetoothError, ClassicAddress, PairingResult}, }; diff --git a/fastpair/rust/bluetooth/src/bluetooth/unsupported/mod.rs b/fastpair/rust/bluetooth/src/unsupported/mod.rs similarity index 100% rename from fastpair/rust/bluetooth/src/bluetooth/unsupported/mod.rs rename to fastpair/rust/bluetooth/src/unsupported/mod.rs diff --git a/fastpair/rust/bluetooth/src/bluetooth/windows/adapter.rs b/fastpair/rust/bluetooth/src/windows/adapter.rs similarity index 99% rename from fastpair/rust/bluetooth/src/bluetooth/windows/adapter.rs rename to fastpair/rust/bluetooth/src/windows/adapter.rs index cf4519f6..7130cb97 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/windows/adapter.rs +++ b/fastpair/rust/bluetooth/src/windows/adapter.rs @@ -53,7 +53,7 @@ use windows::{ Foundation::TypedEventHandler, }; -use crate::bluetooth::{ +use crate::{ api, common::{BleAdvertisement, BleDataTypeId, BluetoothError}, }; diff --git a/fastpair/rust/bluetooth/src/bluetooth/windows/address.rs b/fastpair/rust/bluetooth/src/windows/address.rs similarity index 97% rename from fastpair/rust/bluetooth/src/bluetooth/windows/address.rs rename to fastpair/rust/bluetooth/src/windows/address.rs index d08ae4f3..5423c429 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/windows/address.rs +++ b/fastpair/rust/bluetooth/src/windows/address.rs @@ -16,7 +16,7 @@ //https://learn.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothaddresstype?view=winrt-22621 use windows::Devices::Bluetooth::BluetoothAddressType; -use crate::bluetooth::common::{BleAddressKind, BluetoothError}; +use crate::common::{BleAddressKind, BluetoothError}; // Convenience for converting from Windows API to crate API. impl TryFrom for BleAddressKind { diff --git a/fastpair/rust/bluetooth/src/bluetooth/windows/advertisement.rs b/fastpair/rust/bluetooth/src/windows/advertisement.rs similarity index 99% rename from fastpair/rust/bluetooth/src/bluetooth/windows/advertisement.rs rename to fastpair/rust/bluetooth/src/windows/advertisement.rs index 526e2d5a..dbbdfa2e 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/windows/advertisement.rs +++ b/fastpair/rust/bluetooth/src/windows/advertisement.rs @@ -29,7 +29,7 @@ use windows::{ Storage::Streams::DataReader, }; -use crate::bluetooth::common::{ +use crate::common::{ BleAddress, BleAddressKind, BleAdvertisement, BleDataTypeId, BluetoothError, ServiceData, }; diff --git a/fastpair/rust/bluetooth/src/bluetooth/windows/device.rs b/fastpair/rust/bluetooth/src/windows/device.rs similarity index 98% rename from fastpair/rust/bluetooth/src/bluetooth/windows/device.rs rename to fastpair/rust/bluetooth/src/windows/device.rs index cf0e293f..a3d2d649 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/windows/device.rs +++ b/fastpair/rust/bluetooth/src/windows/device.rs @@ -49,7 +49,7 @@ use windows::{ Foundation::TypedEventHandler, }; -use crate::bluetooth::{api, common::{BleAddress, ClassicAddress, BluetoothError, PairingResult}}; +use crate::{api, common::{BleAddress, ClassicAddress, BluetoothError, PairingResult}}; /// Concrete type implementing `Device`, used for Windows BLE. pub struct BleDevice { diff --git a/fastpair/rust/bluetooth/src/bluetooth/windows/error.rs b/fastpair/rust/bluetooth/src/windows/error.rs similarity index 98% rename from fastpair/rust/bluetooth/src/bluetooth/windows/error.rs rename to fastpair/rust/bluetooth/src/windows/error.rs index 59e12a31..9a7f6d4c 100644 --- a/fastpair/rust/bluetooth/src/bluetooth/windows/error.rs +++ b/fastpair/rust/bluetooth/src/windows/error.rs @@ -14,7 +14,7 @@ use windows::Devices::Enumeration::DevicePairingResultStatus; -use crate::bluetooth::common::{BluetoothError, PairingResult}; +use crate::common::{BluetoothError, PairingResult}; impl From for BluetoothError { fn from(err: windows::core::Error) -> Self { diff --git a/fastpair/rust/bluetooth/src/bluetooth/windows/mod.rs b/fastpair/rust/bluetooth/src/windows/mod.rs similarity index 100% rename from fastpair/rust/bluetooth/src/bluetooth/windows/mod.rs rename to fastpair/rust/bluetooth/src/windows/mod.rs diff --git a/fastpair/rust/bluetooth/tests/integration_test.rs b/fastpair/rust/bluetooth/tests/integration_test.rs index 18021531..b3332b2a 100644 --- a/fastpair/rust/bluetooth/tests/integration_test.rs +++ b/fastpair/rust/bluetooth/tests/integration_test.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use fastpair::*; +use bluetooth::*; mod tests { use super::*; From bf05ab5db4b70af7383ba3ddc47d02ea11f43715 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sat, 29 Jul 2023 22:53:29 -0700 Subject: [PATCH 079/128] [fp-rs] Invoking Rust code from Dart, retrieving stream of incoming bluetooth advertisements. --- .../rust/demo/lib/bridge_definitions.dart | 8 ++- fastpair/rust/demo/lib/bridge_generated.dart | 56 ++++++++++++++---- fastpair/rust/demo/lib/main.dart | 32 +++++----- fastpair/rust/demo/rust/Cargo.toml | 4 ++ fastpair/rust/demo/rust/src/api.rs | 58 ++++++++++++++++++- .../rust/demo/rust/src/bridge_generated.io.rs | 9 ++- .../rust/demo/rust/src/bridge_generated.rs | 18 ++++-- 7 files changed, 147 insertions(+), 38 deletions(-) diff --git a/fastpair/rust/demo/lib/bridge_definitions.dart b/fastpair/rust/demo/lib/bridge_definitions.dart index 9468e02f..6a1b418f 100644 --- a/fastpair/rust/demo/lib/bridge_definitions.dart +++ b/fastpair/rust/demo/lib/bridge_definitions.dart @@ -9,7 +9,11 @@ import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; import 'package:uuid/uuid.dart'; abstract class Rust { - Future hello({dynamic hint}); + Future init({dynamic hint}); - FlutterRustBridgeTaskConstMeta get kHelloConstMeta; + FlutterRustBridgeTaskConstMeta get kInitConstMeta; + + Stream eventStream({dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kEventStreamConstMeta; } diff --git a/fastpair/rust/demo/lib/bridge_generated.dart b/fastpair/rust/demo/lib/bridge_generated.dart index 2c075cb7..c057e0a1 100644 --- a/fastpair/rust/demo/lib/bridge_generated.dart +++ b/fastpair/rust/demo/lib/bridge_generated.dart @@ -25,19 +25,35 @@ class RustImpl implements Rust { factory RustImpl.wasm(FutureOr module) => RustImpl(module as ExternalLibrary); RustImpl.raw(this._platform); - Future hello({dynamic hint}) { + Future init({dynamic hint}) { return _platform.executeNormal(FlutterRustBridgeTask( - callFfi: (port_) => _platform.inner.wire_hello(port_), - parseSuccessData: _wire2api_String, - constMeta: kHelloConstMeta, + callFfi: (port_) => _platform.inner.wire_init(port_), + parseSuccessData: _wire2api_unit, + constMeta: kInitConstMeta, argValues: [], hint: hint, )); } - FlutterRustBridgeTaskConstMeta get kHelloConstMeta => + FlutterRustBridgeTaskConstMeta get kInitConstMeta => const FlutterRustBridgeTaskConstMeta( - debugName: "hello", + debugName: "init", + argNames: [], + ); + + Stream eventStream({dynamic hint}) { + return _platform.executeStream(FlutterRustBridgeTask( + callFfi: (port_) => _platform.inner.wire_event_stream(port_), + parseSuccessData: _wire2api_String, + constMeta: kEventStreamConstMeta, + argValues: [], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kEventStreamConstMeta => + const FlutterRustBridgeTaskConstMeta( + debugName: "event_stream", argNames: [], ); @@ -57,6 +73,10 @@ class RustImpl implements Rust { Uint8List _wire2api_uint_8_list(dynamic raw) { return raw as Uint8List; } + + void _wire2api_unit(dynamic raw) { + return; + } } // Section: api2wire @@ -168,17 +188,31 @@ class RustWire implements FlutterRustBridgeWireBase { late final _init_frb_dart_api_dl = _init_frb_dart_api_dlPtr .asFunction)>(); - void wire_hello( + void wire_init( int port_, ) { - return _wire_hello( + return _wire_init( port_, ); } - late final _wire_helloPtr = - _lookup>('wire_hello'); - late final _wire_hello = _wire_helloPtr.asFunction(); + late final _wire_initPtr = + _lookup>('wire_init'); + late final _wire_init = _wire_initPtr.asFunction(); + + void wire_event_stream( + int port_, + ) { + return _wire_event_stream( + port_, + ); + } + + late final _wire_event_streamPtr = + _lookup>( + 'wire_event_stream'); + late final _wire_event_stream = + _wire_event_streamPtr.asFunction(); void free_WireSyncReturn( WireSyncReturn ptr, diff --git a/fastpair/rust/demo/lib/main.dart b/fastpair/rust/demo/lib/main.dart index 45f354dc..5ce3f31d 100644 --- a/fastpair/rust/demo/lib/main.dart +++ b/fastpair/rust/demo/lib/main.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:demo/rust.dart'; void main() { + api.init(); runApp(const FastPairApp()); } @@ -23,22 +24,19 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) => Scaffold( - appBar: AppBar( - title: const Text("Fast Pair"), + appBar: AppBar(title: const Text("Fast Pair")), + body: Center( + child: StreamBuilder( + // All Rust functions are called as Future's + stream: api.eventStream(), // The Rust function we are calling. + builder: (context, data) { + if (data.hasData) { + return Text(data.data!); // The string to display + } + return const Center( + child: CircularProgressIndicator(), + ); + }, ), - body: Center( - child: FutureBuilder( - // All Rust functions are called as Future's - future: api.hello(), // The Rust function we are calling. - builder: (context, data) { - if (data.hasData) { - return Text(data.data!); // The string to display - } - return const Center( - child: CircularProgressIndicator(), - ); - }, - ), - ), - ); + )); } diff --git a/fastpair/rust/demo/rust/Cargo.toml b/fastpair/rust/demo/rust/Cargo.toml index 41d5928c..20d9a17c 100644 --- a/fastpair/rust/demo/rust/Cargo.toml +++ b/fastpair/rust/demo/rust/Cargo.toml @@ -9,4 +9,8 @@ edition = "2021" crate-type = ["lib", "cdylib", "staticlib"] [dependencies] +anyhow = "1.0" +bluetooth = { version = "0.1", path = "../../bluetooth" } flutter_rust_bridge = "1" +futures = { version = "0.3", features = ["executor"] } +tracing = "0.1.37" diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index 812479a2..dd9ff2d7 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -1,3 +1,57 @@ -pub fn hello() -> String { - String::from("Rust says hi!") +use std::sync::RwLock; + +use bluetooth::{ + api::{BleAdapter, BleDevice}, + BleDataTypeId, Platform, +}; +use flutter_rust_bridge::StreamSink; +use futures::executor; +use tracing::info; + +// Sends a device name to Flutter via `StreamSink` FFI layer. +static NAME_STREAM: RwLock>> = RwLock::new(None); + +/// Sets up initial constructs and infinitely polls for advertisements. +pub fn init() { + let run = async { + info!("start making adapter"); + + let mut adapter = Platform::default_adapter().await.unwrap(); + adapter.start_scan().unwrap(); + + let datatype_selector = vec![BleDataTypeId::ServiceData16BitUuid]; + loop { + let advertisement = adapter + .next_advertisement(Some(&datatype_selector)) + .await + .unwrap(); + + for service_data in advertisement.service_data_16bit_uuid().unwrap() { + let uuid = service_data.uuid(); + // This is a Fast Pair device. + if uuid == 0x2cfe { + let addr = advertisement.address(); + let ble_device = Platform::new_ble_device(addr).await.unwrap(); + let name = ble_device.name().unwrap(); + info!("device: {}", name); + + match NAME_STREAM.read().unwrap().as_ref() { + Some(s) => { + s.add(name); + } + None => info!("Stream is None"), + } + } + } + } + }; + + executor::block_on(run) +} + +/// Sets up `StreamSink` for Dart-Rust FFI. +pub fn event_stream(s: StreamSink) -> Result<(), anyhow::Error> { + let mut stream = NAME_STREAM.write().unwrap(); + *stream = Some(s); + Ok(()) } diff --git a/fastpair/rust/demo/rust/src/bridge_generated.io.rs b/fastpair/rust/demo/rust/src/bridge_generated.io.rs index 5bc455d3..e80e9895 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.io.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.io.rs @@ -2,8 +2,13 @@ use super::*; // Section: wire functions #[no_mangle] -pub extern "C" fn wire_hello(port_: i64) { - wire_hello_impl(port_) +pub extern "C" fn wire_init(port_: i64) { + wire_init_impl(port_) +} + +#[no_mangle] +pub extern "C" fn wire_event_stream(port_: i64) { + wire_event_stream_impl(port_) } // Section: allocate functions diff --git a/fastpair/rust/demo/rust/src/bridge_generated.rs b/fastpair/rust/demo/rust/src/bridge_generated.rs index 710e8db5..1d2f5bd8 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.rs @@ -22,14 +22,24 @@ use std::sync::Arc; // Section: wire functions -fn wire_hello_impl(port_: MessagePort) { - FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, String>( +fn wire_init_impl(port_: MessagePort) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, ()>( WrapInfo { - debug_name: "hello", + debug_name: "init", port: Some(port_), mode: FfiCallMode::Normal, }, - move || move |task_callback| Ok(hello()), + move || move |task_callback| Ok(init()), + ) +} +fn wire_event_stream_impl(port_: MessagePort) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, ()>( + WrapInfo { + debug_name: "event_stream", + port: Some(port_), + mode: FfiCallMode::Stream, + }, + move || move |task_callback| event_stream(task_callback.stream_sink::<_, String>()), ) } // Section: wrapper structs From 0505a23b3f51057120eb0b6648b8116fbd1b8713 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 1 Aug 2023 13:08:11 -0700 Subject: [PATCH 080/128] [fp-rs] Added bluetooth pairing button, result displayed in alert dialog. --- fastpair/rust/bluetooth/src/lib.rs | 3 +- .../rust/demo/lib/bridge_definitions.dart | 4 ++ fastpair/rust/demo/lib/bridge_generated.dart | 28 ++++++++++ fastpair/rust/demo/lib/main.dart | 55 +++++++++++++++++-- fastpair/rust/demo/rust/src/api.rs | 54 +++++++++++++++--- .../rust/demo/rust/src/bridge_generated.io.rs | 5 ++ .../rust/demo/rust/src/bridge_generated.rs | 10 ++++ 7 files changed, 146 insertions(+), 13 deletions(-) diff --git a/fastpair/rust/bluetooth/src/lib.rs b/fastpair/rust/bluetooth/src/lib.rs index e454012b..bc50cc11 100644 --- a/fastpair/rust/bluetooth/src/lib.rs +++ b/fastpair/rust/bluetooth/src/lib.rs @@ -17,7 +17,8 @@ mod common; use api::{BleAdapter, BleDevice, ClassicDevice}; pub use common::{ - BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError, ClassicAddress, + BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError, + ClassicAddress, PairingResult, }; cfg_if::cfg_if! { diff --git a/fastpair/rust/demo/lib/bridge_definitions.dart b/fastpair/rust/demo/lib/bridge_definitions.dart index 6a1b418f..5f59dd77 100644 --- a/fastpair/rust/demo/lib/bridge_definitions.dart +++ b/fastpair/rust/demo/lib/bridge_definitions.dart @@ -16,4 +16,8 @@ abstract class Rust { Stream eventStream({dynamic hint}); FlutterRustBridgeTaskConstMeta get kEventStreamConstMeta; + + Future pair({dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kPairConstMeta; } diff --git a/fastpair/rust/demo/lib/bridge_generated.dart b/fastpair/rust/demo/lib/bridge_generated.dart index c057e0a1..d098d402 100644 --- a/fastpair/rust/demo/lib/bridge_generated.dart +++ b/fastpair/rust/demo/lib/bridge_generated.dart @@ -57,6 +57,22 @@ class RustImpl implements Rust { argNames: [], ); + Future pair({dynamic hint}) { + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => _platform.inner.wire_pair(port_), + parseSuccessData: _wire2api_String, + constMeta: kPairConstMeta, + argValues: [], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kPairConstMeta => + const FlutterRustBridgeTaskConstMeta( + debugName: "pair", + argNames: [], + ); + void dispose() { _platform.dispose(); } @@ -214,6 +230,18 @@ class RustWire implements FlutterRustBridgeWireBase { late final _wire_event_stream = _wire_event_streamPtr.asFunction(); + void wire_pair( + int port_, + ) { + return _wire_pair( + port_, + ); + } + + late final _wire_pairPtr = + _lookup>('wire_pair'); + late final _wire_pair = _wire_pairPtr.asFunction(); + void free_WireSyncReturn( WireSyncReturn ptr, ) { diff --git a/fastpair/rust/demo/lib/main.dart b/fastpair/rust/demo/lib/main.dart index 5ce3f31d..da0bb798 100644 --- a/fastpair/rust/demo/lib/main.dart +++ b/fastpair/rust/demo/lib/main.dart @@ -27,11 +27,56 @@ class HomePage extends StatelessWidget { appBar: AppBar(title: const Text("Fast Pair")), body: Center( child: StreamBuilder( - // All Rust functions are called as Future's - stream: api.eventStream(), // The Rust function we are calling. - builder: (context, data) { - if (data.hasData) { - return Text(data.data!); // The string to display + // Retrieve device stream from Rust side. + stream: api.eventStream(), + builder: (context, deviceName) { + if (deviceName.hasData) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(deviceName.data!), + OutlinedButton( + onPressed: () => showDialog( + context: context, + // Rust functions are invoked as futures. + builder: (context) => FutureBuilder( + future: api.pair(), + builder: (context, pairResult) { + return pairResult.hasData + ? AlertDialog( + title: const Text('Pairing result'), + content: Text(pairResult.data!), + actions: [ + TextButton( + onPressed: () => + Navigator.pop(context, 'OK'), + child: const Text('OK'), + ) + ], + ) + : const AlertDialog( + title: Text('Pairing...'), + // Ensures the progress indicator has sensible dimensions, + // otherwise it follows the height/width of the alert dialog. + content: Column( + mainAxisAlignment: + MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 50, + height: 50, + child: + CircularProgressIndicator(), + ), + ], + ), + ); + })), + child: const Text('Pair'), + ), + ]); } return const Center( child: CircularProgressIndicator(), diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index dd9ff2d7..e826e235 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -1,8 +1,8 @@ use std::sync::RwLock; use bluetooth::{ - api::{BleAdapter, BleDevice}, - BleDataTypeId, Platform, + api::{BleAdapter, BleDevice, ClassicDevice}, + BleAddress, BleDataTypeId, ClassicAddress, PairingResult, Platform, }; use flutter_rust_bridge::StreamSink; use futures::executor; @@ -11,6 +11,9 @@ use tracing::info; // Sends a device name to Flutter via `StreamSink` FFI layer. static NAME_STREAM: RwLock>> = RwLock::new(None); +// Saves the currently displayed device's address, to be used for pairing. +static CURR_ADDRESS: RwLock> = RwLock::new(None); + /// Sets up initial constructs and infinitely polls for advertisements. pub fn init() { let run = async { @@ -34,12 +37,15 @@ pub fn init() { let ble_device = Platform::new_ble_device(addr).await.unwrap(); let name = ble_device.name().unwrap(); info!("device: {}", name); - - match NAME_STREAM.read().unwrap().as_ref() { - Some(s) => { - s.add(name); + if name.contains("LE_WH-1000XM3") { + match NAME_STREAM.read().unwrap().as_ref() { + Some(stream) => { + stream.add(name); + let mut curr_addr = CURR_ADDRESS.write().unwrap(); + *curr_addr = Some(addr); + } + None => info!("Stream is None"), } - None => info!("Stream is None"), } } } @@ -55,3 +61,37 @@ pub fn event_stream(s: StreamSink) -> Result<(), anyhow::Error> { *stream = Some(s); Ok(()) } + +/// Attempt classic pairing with device of address `CURR_ADDRESS`. +pub fn pair() -> String { + let result = match CURR_ADDRESS.read().unwrap().as_ref() { + Some(addr) => { + let run = async { + let classic_addr = ClassicAddress::try_from(*addr).unwrap(); + + let classic_device = Platform::new_classic_device(classic_addr).await.unwrap(); + + match classic_device.pair().await { + Ok(result) => match result { + PairingResult::Success => String::from("Pairing success!"), + PairingResult::AlreadyPaired => { + String::from("This device is already paired.") + } + PairingResult::AlreadyInProgress => { + String::from("Pairing already in progress.") + } + _ => String::from("Unknown result."), + }, + Err(err) => { + format!("Error {}", err) + } + } + }; + + executor::block_on(run) + } + None => String::from("No device available to pair."), + }; + info!(result); + result +} diff --git a/fastpair/rust/demo/rust/src/bridge_generated.io.rs b/fastpair/rust/demo/rust/src/bridge_generated.io.rs index e80e9895..a3892eb9 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.io.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.io.rs @@ -11,6 +11,11 @@ pub extern "C" fn wire_event_stream(port_: i64) { wire_event_stream_impl(port_) } +#[no_mangle] +pub extern "C" fn wire_pair(port_: i64) { + wire_pair_impl(port_) +} + // Section: allocate functions // Section: related functions diff --git a/fastpair/rust/demo/rust/src/bridge_generated.rs b/fastpair/rust/demo/rust/src/bridge_generated.rs index 1d2f5bd8..66635945 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.rs @@ -42,6 +42,16 @@ fn wire_event_stream_impl(port_: MessagePort) { move || move |task_callback| event_stream(task_callback.stream_sink::<_, String>()), ) } +fn wire_pair_impl(port_: MessagePort) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, String>( + WrapInfo { + debug_name: "pair", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || move |task_callback| Ok(pair()), + ) +} // Section: wrapper structs // Section: static checks From f8fb8d4b52815d15a11bb01dd8c2618967b77397 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Wed, 2 Aug 2023 12:12:29 -0700 Subject: [PATCH 081/128] [fp-rs] Flutter UI now only displays the closest discovered device. --- .../bluetooth/src/common/advertisement.rs | 30 ++++- fastpair/rust/bluetooth/src/lib.rs | 2 +- .../bluetooth/src/windows/advertisement.rs | 9 +- fastpair/rust/demo/rust/src/advertisement.rs | 89 +++++++++++++ fastpair/rust/demo/rust/src/api.rs | 124 ++++++++++++++---- fastpair/rust/demo/rust/src/lib.rs | 3 +- 6 files changed, 228 insertions(+), 29 deletions(-) create mode 100644 fastpair/rust/demo/rust/src/advertisement.rs diff --git a/fastpair/rust/bluetooth/src/common/advertisement.rs b/fastpair/rust/bluetooth/src/common/advertisement.rs index 8260936e..9fb7f999 100644 --- a/fastpair/rust/bluetooth/src/common/advertisement.rs +++ b/fastpair/rust/bluetooth/src/common/advertisement.rs @@ -18,16 +18,30 @@ use super::{BleAddress, BluetoothError}; /// information about the advertisement (e.g. address of sender) as well as /// data sections extracted from the advertisement. Platform-specific methods /// should be written to load in data sections from incoming advertisements. +#[derive(Clone)] pub struct BleAdvertisement { address: BleAddress, + rssi: Option, + tx_power: Option, service_data_16bit_uuid: Option>>, } +/// Decibel-milliwatt or dBm is a dimensionless absolute unit expressing the +/// power of a signal relative to one milliwatt (mW). The unit is in log10, i.e. +/// 1 mW is 0 dBm and a 10 dBm increase represents a ten-fold increase in power. +type DecibelMilliwatts = i16; + impl BleAdvertisement { /// Construct a new `BleAdvertisement` instance. - pub(crate) fn new(address: BleAddress) -> Self { + pub(crate) fn new( + address: BleAddress, + rssi: Option, + tx_power: Option, + ) -> Self { BleAdvertisement { address, + rssi, + tx_power, service_data_16bit_uuid: None, } } @@ -37,6 +51,19 @@ impl BleAdvertisement { self.address } + /// Retrieve the Received Signal Strength Indicator (RSSI) value for this + /// advertisement, expressed in dBm. The RSSI might be the raw value or the + /// filtered RSSI, depending on the configured signal strength filter. + pub fn rssi(&self) -> Option { + self.rssi + } + + /// Retrieve the transmit power advertised by this device, if any. + /// For BLE communication, values will range from -127 dBm to 20 dBm. + pub fn tx_power(&self) -> Option { + self.tx_power + } + /// Setter for `ServiceData` field with 16bit UUID. pub(crate) fn set_service_data_16bit_uuid( &mut self, @@ -69,6 +96,7 @@ pub enum BleDataTypeId { /// Struct representing the Bluetooth Service Data common data type. `U` should /// be one of the valid uuid sizes, specified in: /// Bluetooth Supplement to the Core Specification, Part A, Section 1.11. +#[derive(Clone)] pub struct ServiceData { uuid: U, data: Vec, diff --git a/fastpair/rust/bluetooth/src/lib.rs b/fastpair/rust/bluetooth/src/lib.rs index bc50cc11..ebe8384a 100644 --- a/fastpair/rust/bluetooth/src/lib.rs +++ b/fastpair/rust/bluetooth/src/lib.rs @@ -18,7 +18,7 @@ mod common; use api::{BleAdapter, BleDevice, ClassicDevice}; pub use common::{ BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError, - ClassicAddress, PairingResult, + ClassicAddress, PairingResult, ServiceData, }; cfg_if::cfg_if! { diff --git a/fastpair/rust/bluetooth/src/windows/advertisement.rs b/fastpair/rust/bluetooth/src/windows/advertisement.rs index dbbdfa2e..bf80b5a3 100644 --- a/fastpair/rust/bluetooth/src/windows/advertisement.rs +++ b/fastpair/rust/bluetooth/src/windows/advertisement.rs @@ -44,8 +44,15 @@ impl TryFrom<&BluetoothLEAdvertisementReceivedEventArgs> for BleAdvertisement { let kind = BleAddressKind::try_from(adv.BluetoothAddressType()?)?; let addr = BleAddress::new(addr, kind); + // `rssi` and tx_power` aren't always advertised, so convert to None if + // can't extract value. + let rssi = adv.RawSignalStrengthInDBm().ok(); + let tx_power = match adv.TransmitPowerLevelInDBm() { + Ok(val_ref) => val_ref.GetInt16().ok(), + Err(_) => None, + }; - Ok(BleAdvertisement::new(addr)) + Ok(BleAdvertisement::new(addr, rssi, tx_power)) } } diff --git a/fastpair/rust/demo/rust/src/advertisement.rs b/fastpair/rust/demo/rust/src/advertisement.rs new file mode 100644 index 00000000..c94dd4c4 --- /dev/null +++ b/fastpair/rust/demo/rust/src/advertisement.rs @@ -0,0 +1,89 @@ +// 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. + +use bluetooth::{BleAddress, BleAdvertisement}; + +/// Holds information required to make decisions about an incoming Fast Pair +/// advertisement. +#[derive(Clone)] +pub(crate) struct FpPairingAdvertisement { + inner: BleAdvertisement, + /// Estimated distance in meters of device from BLE adapter. + distance: f64, +} + +impl FpPairingAdvertisement { + /// Retrieve estimated distance the BLE advertisement travelled between + /// the sending device and this receiver. + pub(crate) fn distance(&self) -> f64 { + self.distance + } + + /// Retrieve the BLE Address of the advertising device. + pub(crate) fn address(&self) -> BleAddress { + self.inner.address() + } +} + +impl TryFrom for FpPairingAdvertisement { + type Error = anyhow::Error; + + fn try_from(adv: BleAdvertisement) -> Result { + let rssi = adv.rssi().ok_or(anyhow::anyhow!( + "Windows advertisements should contain RSSI information." + ))?; + let tx_power = adv.tx_power().ok_or(anyhow::anyhow!( + "Fast Pair advertisements should advertise their transmit power." + ))?; + + let distance = distance_from_rssi_and_tx_power(rssi, tx_power); + + Ok(FpPairingAdvertisement { + inner: adv, + distance, + }) + } +} + +/// Convert RSSI and transmit power to distance using log-distance path loss +/// model, with reference path loss of 1m at 41dB in free space. +/// See: https://en.wikipedia.org/wiki/Log-distance_path_loss_model. +#[inline] +pub(crate) fn distance_from_rssi_and_tx_power(rssi: i16, tx_power: i16) -> f64 { + // Source: Android Nearby implementation, `RangingUtils.java`. + // + // PL = total path loss in db + // txPower = TxPower in dbm + // rssi = Received signal strength in dbm + // PL_0 = Path loss at reference distance d_0 {@link RSSI_DROP_OFF_AT_1_M} dbm + // d = length of path + // d_0 = reference distance (1 m) + // gamma = path loss exponent (2 in free space) + // + // Log-distance path loss (LDPL) formula: + // + // PL = txPower - rssi = PL_0 + 10 * gamma * log_10(d / d_0) + // txPower - rssi = RSSI_DROP_OFF_AT_1_M + 10 * gamma * log_10(d / d_0) + // txPower - rssi - RSSI_DROP_OFF_AT_1_M = 10 * 2 * log_10(distanceInMeters / 1) + // txPower - rssi - RSSI_DROP_OFF_AT_1_M = 20 * log_10(distanceInMeters / 1) + // (txPower - rssi - RSSI_DROP_OFF_AT_1_M) / 20 = log_10(distanceInMeters) + // 10 ^ ((txPower - rssi - RSSI_DROP_OFF_AT_1_M) / 20) = distanceInMeters + + const RSSI_DROPOFF_AT_1_M: i16 = 41; + const PATH_LOSS_EXPONENT: i16 = 2; + + f64::from(10.0).powf( + (f64::from(tx_power - rssi - RSSI_DROPOFF_AT_1_M)) / f64::from(10 * PATH_LOSS_EXPONENT), + ) +} diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index e826e235..d4abaffa 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -1,18 +1,100 @@ -use std::sync::RwLock; +use std::{collections::HashMap, sync::RwLock}; use bluetooth::{ api::{BleAdapter, BleDevice, ClassicDevice}, - BleAddress, BleDataTypeId, ClassicAddress, PairingResult, Platform, + BleAddress, BleAdvertisement, BleDataTypeId, ClassicAddress, PairingResult, Platform, + ServiceData, }; use flutter_rust_bridge::StreamSink; use futures::executor; -use tracing::info; +use tracing::{info, warn}; + +use crate::advertisement::FpPairingAdvertisement; // Sends a device name to Flutter via `StreamSink` FFI layer. static NAME_STREAM: RwLock>> = RwLock::new(None); -// Saves the currently displayed device's address, to be used for pairing. -static CURR_ADDRESS: RwLock> = RwLock::new(None); +// Saves the currently displayed device's advertisement, to be used for pairing. +static CURR_DEVICE_ADV: RwLock> = RwLock::new(None); + +/// Updates the device name as displayed by Flutter. +#[inline] +async fn update_best_device(best_adv: FpPairingAdvertisement) { + let addr = best_adv.address(); + let ble_device = Platform::new_ble_device(addr).await.unwrap(); + let name = ble_device.name().unwrap(); + + match NAME_STREAM.read().unwrap().as_ref() { + Some(stream) => { + stream.add(name); + } + None => info!("Stream is None"), + } + let mut curr_adv = CURR_DEVICE_ADV.write().unwrap(); + *curr_adv = Some(best_adv); +} + +/// Determines whether the device advertised by the provided service data is the +/// closest Fast Pair device. +/// If this device has been seen previously but has now moved further away, +/// decide which other seen device is now closer. +#[inline] +fn new_best_fp_advertisement( + advertisement: BleAdvertisement, + service_data: &ServiceData, + latest_advertisement_map: &mut HashMap, +) -> Option { + // Analyze service data sections. + let uuid = service_data.uuid(); + + // This is not a Fast Pair device. + if uuid != 0x2cfe { + return None; + } + + let fp_adv = match FpPairingAdvertisement::try_from(advertisement) { + Ok(fp_adv) => fp_adv, + Err(err) => { + // If error during construction (e.g. non-discoverable + // Fast Pair device not advertising tx_power or + // with service data that isn't model ID) ignore + // this advertisement section. + warn!("Error creating FP Advertisement: {}", err); + return None; + } + }; + + latest_advertisement_map.insert(fp_adv.address(), fp_adv.clone()); + + if let Some(best_adv) = CURR_DEVICE_ADV.read().unwrap().as_ref() { + if best_adv.distance() >= fp_adv.distance() { + // New advertised distance is closer. + Some(fp_adv) + } else if best_adv.address() == fp_adv.address() { + // New advertised distance by the previous best device has + // increased, so select new closest device. + let next_best_adv_ref = + latest_advertisement_map + .values() + .into_iter() + .min_by(|adv1, adv2| { + // We should never get NaN, so it's okay to unwrap. + adv1.distance().partial_cmp(&adv2.distance()).unwrap() + }); + + if let Some(next_best_adv) = next_best_adv_ref { + Some(next_best_adv.to_owned()) + } else { + None + } + } else { + None + } + } else { + // First discovered device, must be closest. + Some(fp_adv) + } +} /// Sets up initial constructs and infinitely polls for advertisements. pub fn init() { @@ -22,31 +104,23 @@ pub fn init() { let mut adapter = Platform::default_adapter().await.unwrap(); adapter.start_scan().unwrap(); + let mut latest_advertisement_map = HashMap::new(); let datatype_selector = vec![BleDataTypeId::ServiceData16BitUuid]; + loop { + // Retrieve the next received advertisement. let advertisement = adapter .next_advertisement(Some(&datatype_selector)) .await .unwrap(); for service_data in advertisement.service_data_16bit_uuid().unwrap() { - let uuid = service_data.uuid(); - // This is a Fast Pair device. - if uuid == 0x2cfe { - let addr = advertisement.address(); - let ble_device = Platform::new_ble_device(addr).await.unwrap(); - let name = ble_device.name().unwrap(); - info!("device: {}", name); - if name.contains("LE_WH-1000XM3") { - match NAME_STREAM.read().unwrap().as_ref() { - Some(stream) => { - stream.add(name); - let mut curr_addr = CURR_ADDRESS.write().unwrap(); - *curr_addr = Some(addr); - } - None => info!("Stream is None"), - } - } + if let Some(best_adv) = new_best_fp_advertisement( + advertisement.clone(), + service_data, + &mut latest_advertisement_map, + ) { + update_best_device(best_adv).await; } } } @@ -64,10 +138,10 @@ pub fn event_stream(s: StreamSink) -> Result<(), anyhow::Error> { /// Attempt classic pairing with device of address `CURR_ADDRESS`. pub fn pair() -> String { - let result = match CURR_ADDRESS.read().unwrap().as_ref() { - Some(addr) => { + let result = match CURR_DEVICE_ADV.read().unwrap().as_ref() { + Some(adv) => { let run = async { - let classic_addr = ClassicAddress::try_from(*addr).unwrap(); + let classic_addr = ClassicAddress::try_from(adv.address()).unwrap(); let classic_device = Platform::new_classic_device(classic_addr).await.unwrap(); diff --git a/fastpair/rust/demo/rust/src/lib.rs b/fastpair/rust/demo/rust/src/lib.rs index 97e8ba25..8e78a507 100644 --- a/fastpair/rust/demo/rust/src/lib.rs +++ b/fastpair/rust/demo/rust/src/lib.rs @@ -1,2 +1,3 @@ -mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ +mod advertisement; mod api; +mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ From fabeff0d570cc4bbf027e85eb2b395da6ff9e0fb Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Mon, 14 Aug 2023 09:03:47 -0700 Subject: [PATCH 082/128] Added APIs to control device sleep These APIs can be used to prevent device into sleep when sending supper big files. PiperOrigin-RevId: 556809236 --- internal/platform/device_info.h | 3 +++ internal/platform/device_info_impl.cc | 6 ++++++ internal/platform/device_info_impl.h | 3 +++ .../platform/implementation/apple/device_info.h | 14 ++++++++++++++ .../platform/implementation/apple/device_info.mm | 4 ++++ internal/platform/implementation/device_info.h | 4 ++++ internal/platform/implementation/g3/device_info.h | 6 +++++- .../platform/implementation/windows/device_info.cc | 10 ++++++++++ .../platform/implementation/windows/device_info.h | 3 +++ .../implementation/windows/device_info_test.cc | 8 ++++++++ internal/test/fake_device_info.h | 4 ++++ internal/test/fake_device_info_test.cc | 10 ++++++++++ 12 files changed, 74 insertions(+), 1 deletion(-) diff --git a/internal/platform/device_info.h b/internal/platform/device_info.h index 88254734..c95f592d 100644 --- a/internal/platform/device_info.h +++ b/internal/platform/device_info.h @@ -52,6 +52,9 @@ class DeviceInfo { virtual void UnregisterScreenLockedListener( absl::string_view listener_name) = 0; + virtual bool PreventSleep() = 0; + virtual bool AllowSleep() = 0; + // Returns localized device name depends on device type. std::u16string GetDeviceTypeName() const { // TODO(b/230132370): return localized device name. diff --git a/internal/platform/device_info_impl.cc b/internal/platform/device_info_impl.cc index 702c83a0..6ac551d5 100644 --- a/internal/platform/device_info_impl.cc +++ b/internal/platform/device_info_impl.cc @@ -108,4 +108,10 @@ void DeviceInfoImpl::UnregisterScreenLockedListener( device_info_impl_->UnregisterScreenLockedListener(listener_name); } +bool DeviceInfoImpl::PreventSleep() { + return device_info_impl_->PreventSleep(); +} + +bool DeviceInfoImpl::AllowSleep() { return device_info_impl_->AllowSleep(); } + } // namespace nearby diff --git a/internal/platform/device_info_impl.h b/internal/platform/device_info_impl.h index 1c44d961..98e6032f 100644 --- a/internal/platform/device_info_impl.h +++ b/internal/platform/device_info_impl.h @@ -53,6 +53,9 @@ class DeviceInfoImpl : public DeviceInfo { 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_; }; diff --git a/internal/platform/implementation/apple/device_info.h b/internal/platform/implementation/apple/device_info.h index 953f54c4..4315040c 100644 --- a/internal/platform/implementation/apple/device_info.h +++ b/internal/platform/implementation/apple/device_info.h @@ -58,6 +58,20 @@ class DeviceInfo : public api::DeviceInfo { std::function callback) override; void UnregisterScreenLockedListener(absl::string_view listener_name) override; + + // Request the system to prevent the device from going to sleep. + // + // To allow the system to sleep again, call @c AllowSleep(). + // + // Returns @c true on success or if the platform does not allow preventing + // sleep. Returns @c false on any other error. + bool PreventSleep() override; + + // Release any requests preventing the device from going to sleep. + // + // Returns @c true on success or if the system is already allowed to sleep. + // Returns @c false if an error occurred. + bool AllowSleep() override; }; } // namespace apple diff --git a/internal/platform/implementation/apple/device_info.mm b/internal/platform/implementation/apple/device_info.mm index b5053759..ee0104dd 100644 --- a/internal/platform/implementation/apple/device_info.mm +++ b/internal/platform/implementation/apple/device_info.mm @@ -168,5 +168,9 @@ void DeviceInfo::RegisterScreenLockedListener( void DeviceInfo::UnregisterScreenLockedListener(absl::string_view listener_name) {} +bool DeviceInfo::PreventSleep() { return true; } + +bool DeviceInfo::AllowSleep() { return true; } + } // namespace apple } // namespace nearby diff --git a/internal/platform/implementation/device_info.h b/internal/platform/implementation/device_info.h index 7247fa85..b4a0ba4b 100644 --- a/internal/platform/implementation/device_info.h +++ b/internal/platform/implementation/device_info.h @@ -59,6 +59,10 @@ class DeviceInfo { std::function callback) = 0; virtual void UnregisterScreenLockedListener( absl::string_view listener_name) = 0; + + // Control device sleep + virtual bool PreventSleep() = 0; + virtual bool AllowSleep() = 0; }; } // namespace api diff --git a/internal/platform/implementation/g3/device_info.h b/internal/platform/implementation/g3/device_info.h index e9a90ff3..87d6a958 100644 --- a/internal/platform/implementation/g3/device_info.h +++ b/internal/platform/implementation/g3/device_info.h @@ -38,7 +38,7 @@ class DeviceInfo : public api::DeviceInfo { return api::DeviceInfo::DeviceType::kLaptop; } - api::DeviceInfo::OsType GetOsType() const override{ + api::DeviceInfo::OsType GetOsType() const override { return api::DeviceInfo::OsType::kChromeOs; } @@ -92,6 +92,10 @@ class DeviceInfo : public api::DeviceInfo { screen_locked_listeners_.erase(listener_name); } + bool PreventSleep() override { return true; } + + bool AllowSleep() override { return true; } + private: absl::flat_hash_map> diff --git a/internal/platform/implementation/windows/device_info.cc b/internal/platform/implementation/windows/device_info.cc index eb3220c5..5978f27c 100644 --- a/internal/platform/implementation/windows/device_info.cc +++ b/internal/platform/implementation/windows/device_info.cc @@ -353,5 +353,15 @@ void DeviceInfo::UnregisterScreenLockedListener( session_manager_.UnregisterSessionListener(listener_name); } +bool DeviceInfo::PreventSleep() { + absl::MutexLock lock(&mutex_); + return session_manager_.PreventSleep(); +} + +bool DeviceInfo::AllowSleep() { + absl::MutexLock lock(&mutex_); + return session_manager_.AllowSleep(); +} + } // namespace windows } // namespace nearby diff --git a/internal/platform/implementation/windows/device_info.h b/internal/platform/implementation/windows/device_info.h index 9d112d30..79c96771 100644 --- a/internal/platform/implementation/windows/device_info.h +++ b/internal/platform/implementation/windows/device_info.h @@ -52,6 +52,9 @@ class DeviceInfo : public api::DeviceInfo { std::function callback) override; void UnregisterScreenLockedListener(absl::string_view listener_name) override; + bool PreventSleep() override; + bool AllowSleep() override; + private: mutable absl::Mutex mutex_; SessionManager session_manager_ ABSL_GUARDED_BY(mutex_); diff --git a/internal/platform/implementation/windows/device_info_test.cc b/internal/platform/implementation/windows/device_info_test.cc index 4cdbf306..d19c4370 100644 --- a/internal/platform/implementation/windows/device_info_test.cc +++ b/internal/platform/implementation/windows/device_info_test.cc @@ -77,6 +77,14 @@ TEST(DeviceInfo, DISABLED_IsScreenLocked) { EXPECT_FALSE(DeviceInfo().IsScreenLocked()); } +TEST(DeviceInfo, DISABLED_PreventSleep) { + EXPECT_TRUE(DeviceInfo().PreventSleep()); +} + +TEST(DeviceInfo, DISABLED_AllowSleep) { + EXPECT_TRUE(DeviceInfo().AllowSleep()); +} + } // namespace } // namespace windows } // namespace nearby diff --git a/internal/test/fake_device_info.h b/internal/test/fake_device_info.h index 4f296702..b7fbf160 100644 --- a/internal/test/fake_device_info.h +++ b/internal/test/fake_device_info.h @@ -86,6 +86,10 @@ class FakeDeviceInfo : public DeviceInfo { screen_locked_listeners_.erase(listener_name); } + bool PreventSleep() override { return true; } + + bool AllowSleep() override { return true; } + int GetScreenLockedListenerCount() { return screen_locked_listeners_.size(); } // Mock methods. diff --git a/internal/test/fake_device_info_test.cc b/internal/test/fake_device_info_test.cc index 53ffc093..16935c43 100644 --- a/internal/test/fake_device_info_test.cc +++ b/internal/test/fake_device_info_test.cc @@ -188,5 +188,15 @@ TEST(FakeDeviceInfo, UpdateScreenLockedListener) { EXPECT_EQ(screen_locked_tracker_2, api::DeviceInfo::ScreenStatus::kUnlocked); } +TEST(FakeDeviceInfo, PreventSleep) { + FakeDeviceInfo device_info; + EXPECT_TRUE(device_info.PreventSleep()); +} + +TEST(FakeDeviceInfo, AllowSleep) { + FakeDeviceInfo device_info; + EXPECT_TRUE(device_info.AllowSleep()); +} + } // namespace } // namespace nearby From a57b55ad98ef129caeb2c2c1f439fa21960f0b40 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Thu, 3 Aug 2023 13:12:51 -0700 Subject: [PATCH 083/128] [fp-rs] Now using model ID instead of BLE address for keeping track of unique devices. --- fastpair/rust/demo/rust/Cargo.toml | 2 + fastpair/rust/demo/rust/src/advertisement.rs | 79 +++++++++++++++----- fastpair/rust/demo/rust/src/api.rs | 11 ++- fastpair/rust/demo/rust/src/decoder.rs | 50 +++++++++++++ fastpair/rust/demo/rust/src/lib.rs | 3 +- 5 files changed, 119 insertions(+), 26 deletions(-) create mode 100644 fastpair/rust/demo/rust/src/decoder.rs diff --git a/fastpair/rust/demo/rust/Cargo.toml b/fastpair/rust/demo/rust/Cargo.toml index 20d9a17c..23463338 100644 --- a/fastpair/rust/demo/rust/Cargo.toml +++ b/fastpair/rust/demo/rust/Cargo.toml @@ -13,4 +13,6 @@ anyhow = "1.0" bluetooth = { version = "0.1", path = "../../bluetooth" } flutter_rust_bridge = "1" futures = { version = "0.3", features = ["executor"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" tracing = "0.1.37" diff --git a/fastpair/rust/demo/rust/src/advertisement.rs b/fastpair/rust/demo/rust/src/advertisement.rs index c94dd4c4..46aae17b 100644 --- a/fastpair/rust/demo/rust/src/advertisement.rs +++ b/fastpair/rust/demo/rust/src/advertisement.rs @@ -12,7 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use bluetooth::{BleAddress, BleAdvertisement}; +use bluetooth::{BleAddress, BleAdvertisement, ServiceData}; + +use crate::decoder::FpDecoder; + +/// Represents a FP device model ID. +pub(crate) type ModelId = String; /// Holds information required to make decisions about an incoming Fast Pair /// advertisement. @@ -21,9 +26,59 @@ pub(crate) struct FpPairingAdvertisement { inner: BleAdvertisement, /// Estimated distance in meters of device from BLE adapter. distance: f64, + model_id: ModelId, } impl FpPairingAdvertisement { + /// Create a new Fast Pair advertisement instance. + pub(crate) fn new( + adv: BleAdvertisement, + service_data: &ServiceData, + ) -> Result { + let rssi = adv.rssi().ok_or(anyhow::anyhow!( + "Windows advertisements should contain RSSI information." + ))?; + let tx_power = adv.tx_power().ok_or(anyhow::anyhow!( + "Fast Pair advertisements should advertise their transmit power." + ))?; + + let distance = distance_from_rssi_and_tx_power(rssi, tx_power); + + // Extract model ID from service data. We don't need to store service + // data in the `FpPairingAdvertisement` since it's easily accessible from + // `FpPairingAdvertisement.inner`, but it's convenient to save the parsed + // model ID. + let mut model_id = + FpDecoder::get_model_id_from_service_data(service_data).or_else(|err| { + // Some FP advertisements can be GATT non-discoverable + // advertisements containing service data that isn't + // the device model ID. In this case, simply ignore + // advertisements with errors extracting the model ID. + // See: developers.google.com/nearby/fast-pair/specifications/service/provider + Err(anyhow::anyhow!("error extracting model ID: {}", err)) + })?; + + if model_id.len() != 3 { + // In this demo of Fast Pair Rust, only model ID's + // of length 3 bytes are supported. Therefore, if a + // larger model ID makes it this far, log an error. + // TODO b/294453912 + return Err(anyhow::anyhow!("Error: model ID of unsupported length")); + } + + // Pad with 0 at the beginning to successfully call `from_be_bytes`. + // Assumes `model_id.len() == 3` before the call to `insert`, otherwise + // this will panic. + model_id.insert(0, 0); + let model_id = format!("{}", u32::from_be_bytes(model_id.try_into().unwrap())); + + Ok(FpPairingAdvertisement { + inner: adv, + distance, + model_id, + }) + } + /// Retrieve estimated distance the BLE advertisement travelled between /// the sending device and this receiver. pub(crate) fn distance(&self) -> f64 { @@ -34,25 +89,11 @@ impl FpPairingAdvertisement { pub(crate) fn address(&self) -> BleAddress { self.inner.address() } -} -impl TryFrom for FpPairingAdvertisement { - type Error = anyhow::Error; - - fn try_from(adv: BleAdvertisement) -> Result { - let rssi = adv.rssi().ok_or(anyhow::anyhow!( - "Windows advertisements should contain RSSI information." - ))?; - let tx_power = adv.tx_power().ok_or(anyhow::anyhow!( - "Fast Pair advertisements should advertise their transmit power." - ))?; - - let distance = distance_from_rssi_and_tx_power(rssi, tx_power); - - Ok(FpPairingAdvertisement { - inner: adv, - distance, - }) + /// Retrieve the Model ID advertised by this device, parsed from the + /// 16-bit UUID service data. + pub(crate) fn model_id(&self) -> &ModelId { + &self.model_id } } diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index d4abaffa..6e94476d 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -2,8 +2,7 @@ use std::{collections::HashMap, sync::RwLock}; use bluetooth::{ api::{BleAdapter, BleDevice, ClassicDevice}, - BleAddress, BleAdvertisement, BleDataTypeId, ClassicAddress, PairingResult, Platform, - ServiceData, + BleAdvertisement, BleDataTypeId, ClassicAddress, PairingResult, Platform, ServiceData, }; use flutter_rust_bridge::StreamSink; use futures::executor; @@ -42,7 +41,7 @@ async fn update_best_device(best_adv: FpPairingAdvertisement) { fn new_best_fp_advertisement( advertisement: BleAdvertisement, service_data: &ServiceData, - latest_advertisement_map: &mut HashMap, + latest_advertisement_map: &mut HashMap, ) -> Option { // Analyze service data sections. let uuid = service_data.uuid(); @@ -52,7 +51,7 @@ fn new_best_fp_advertisement( return None; } - let fp_adv = match FpPairingAdvertisement::try_from(advertisement) { + let fp_adv = match FpPairingAdvertisement::new(advertisement, service_data) { Ok(fp_adv) => fp_adv, Err(err) => { // If error during construction (e.g. non-discoverable @@ -64,13 +63,13 @@ fn new_best_fp_advertisement( } }; - latest_advertisement_map.insert(fp_adv.address(), fp_adv.clone()); + latest_advertisement_map.insert(fp_adv.model_id().to_owned(), fp_adv.clone()); if let Some(best_adv) = CURR_DEVICE_ADV.read().unwrap().as_ref() { if best_adv.distance() >= fp_adv.distance() { // New advertised distance is closer. Some(fp_adv) - } else if best_adv.address() == fp_adv.address() { + } else if best_adv.model_id() == fp_adv.model_id() { // New advertised distance by the previous best device has // increased, so select new closest device. let next_best_adv_ref = diff --git a/fastpair/rust/demo/rust/src/decoder.rs b/fastpair/rust/demo/rust/src/decoder.rs new file mode 100644 index 00000000..9c162937 --- /dev/null +++ b/fastpair/rust/demo/rust/src/decoder.rs @@ -0,0 +1,50 @@ +// 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. + +use bluetooth::ServiceData; + +/// Unit struct providing parsing operations for Fast Pair advertisements. +pub(crate) struct FpDecoder; + +impl FpDecoder { + /// Retrieve the Fast Pair device model ID from a service data payload. + /// https://developers.google.com/nearby/fast-pair/specifications/service/provider. + /// * Length < 3: invalid payload + /// * Length == 3: entire payload is the model ID + /// * Length > 3: first byte specifies the length of the model ID, in bytes. + /// Currently unavailable in Fast Pair devices and not supported. + pub(crate) fn get_model_id_from_service_data( + service_data: &ServiceData, + ) -> Result, anyhow::Error> { + static MIN_MODEL_ID_LENGTH: usize = 3; + let data = service_data.data(); + + if data.len() < MIN_MODEL_ID_LENGTH { + // If service data too small, invalid payload. + Err(anyhow::anyhow!(format!( + "Invalid model ID for Fast Pair advertisement of length {}.", + data.len() + ))) + } else if data.len() == MIN_MODEL_ID_LENGTH { + // Else if service data length is exactly 3, all bytes are the ID. + Ok(data.clone()) + } else { + // Else, this Fast Pair advertisement is currently unsupported. + // b/294453912 + Err(anyhow::anyhow!( + "This Fast Pair device is currently unsupported." + )) + } + } +} diff --git a/fastpair/rust/demo/rust/src/lib.rs b/fastpair/rust/demo/rust/src/lib.rs index 8e78a507..c4831ae7 100644 --- a/fastpair/rust/demo/rust/src/lib.rs +++ b/fastpair/rust/demo/rust/src/lib.rs @@ -1,3 +1,4 @@ mod advertisement; mod api; -mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ +mod bridge_generated; +mod decoder; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ From 329e1b298e8db96557f184c2c2fc3d32f122dc75 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Mon, 7 Aug 2023 12:05:06 -0700 Subject: [PATCH 084/128] [fp-rs] Retrieving device information from model ID by parsing local file with serde. --- fastpair/rust/demo/rust/src/decoder.rs | 1 - fastpair/rust/demo/rust/src/fetcher.rs | 67 ++++++++++++++++++++++++++ fastpair/rust/demo/rust/src/lib.rs | 5 +- 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 fastpair/rust/demo/rust/src/fetcher.rs diff --git a/fastpair/rust/demo/rust/src/decoder.rs b/fastpair/rust/demo/rust/src/decoder.rs index 9c162937..dd0f80d4 100644 --- a/fastpair/rust/demo/rust/src/decoder.rs +++ b/fastpair/rust/demo/rust/src/decoder.rs @@ -11,7 +11,6 @@ // 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 bluetooth::ServiceData; /// Unit struct providing parsing operations for Fast Pair advertisements. diff --git a/fastpair/rust/demo/rust/src/fetcher.rs b/fastpair/rust/demo/rust/src/fetcher.rs new file mode 100644 index 00000000..7eb9d09b --- /dev/null +++ b/fastpair/rust/demo/rust/src/fetcher.rs @@ -0,0 +1,67 @@ +// 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. +use std::fs; + +use serde::Deserialize; + +use crate::advertisement::ModelId; + +/// Holds Fast Pair device information parsed from JSON. +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct DeviceInfo { + image_url: String, + name: String, +} + +/// Holds top-level Fast Pair information parsed from JSON. See `local` +/// directory for format. +#[derive(Deserialize)] +struct JsonData { + device: DeviceInfo, +} + +/// Types that can fetch Fast Pair data from external storage (e.g. filesystem, +/// remote server). +pub(crate) trait FpFetcher { + fn get_device_info_from_model_id(model_id: &ModelId) -> Result; +} + +/// A unit struct for retrieving Fast Pair information from the local filesystem. +pub(crate) struct FpFetcherLocal; + +impl FpFetcher for FpFetcherLocal { + /// Retrieve device information for the provided Model ID. Currently, + /// this information is saved locally. In the future, this should instead + /// be retrieved from a remote server and cached. + /// b/294456411 + fn get_device_info_from_model_id(model_id: &ModelId) -> Result { + let file_path = format!("./local/{model_id}.json"); + let contents = fs::read_to_string(file_path).expect("Couldn't find or open file."); + + let model_info: JsonData = serde_json::from_str(&contents)?; + + Ok(model_info.device) + } +} + +impl DeviceInfo { + pub(crate) fn name(&self) -> &String { + &self.name + } + + pub(crate) fn image_url(&self) -> &String { + &self.image_url + } +} diff --git a/fastpair/rust/demo/rust/src/lib.rs b/fastpair/rust/demo/rust/src/lib.rs index c4831ae7..663b166e 100644 --- a/fastpair/rust/demo/rust/src/lib.rs +++ b/fastpair/rust/demo/rust/src/lib.rs @@ -1,4 +1,5 @@ mod advertisement; mod api; -mod bridge_generated; -mod decoder; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ +mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ +mod decoder; +mod fetcher; From 57373a80c3814d7dcf1618b13be06b7db853c714 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Mon, 7 Aug 2023 13:05:58 -0700 Subject: [PATCH 085/128] [fp-rs] Displaying device image retrieved from model ID. --- .../rust/demo/lib/bridge_definitions.dart | 16 +++++++- fastpair/rust/demo/lib/bridge_generated.dart | 12 +++++- fastpair/rust/demo/lib/main.dart | 16 +++++--- fastpair/rust/demo/local/706908.json | 40 +++++++++++++++++++ fastpair/rust/demo/rust/src/advertisement.rs | 23 ++++++++++- fastpair/rust/demo/rust/src/api.rs | 21 +++++----- .../rust/demo/rust/src/bridge_generated.rs | 2 +- fastpair/rust/demo/rust/src/fetcher.rs | 22 ++++++++-- 8 files changed, 127 insertions(+), 25 deletions(-) create mode 100644 fastpair/rust/demo/local/706908.json diff --git a/fastpair/rust/demo/lib/bridge_definitions.dart b/fastpair/rust/demo/lib/bridge_definitions.dart index 5f59dd77..5e44a0de 100644 --- a/fastpair/rust/demo/lib/bridge_definitions.dart +++ b/fastpair/rust/demo/lib/bridge_definitions.dart @@ -8,16 +8,30 @@ import 'package:meta/meta.dart'; import 'package:flutter_rust_bridge/flutter_rust_bridge.dart'; import 'package:uuid/uuid.dart'; +import 'package:collection/collection.dart'; + abstract class Rust { + /// Sets up initial constructs and infinitely polls for advertisements. Future init({dynamic hint}); FlutterRustBridgeTaskConstMeta get kInitConstMeta; - Stream eventStream({dynamic hint}); + /// Sets up `StreamSink` for Dart-Rust FFI. + Stream eventStream({dynamic hint}); FlutterRustBridgeTaskConstMeta get kEventStreamConstMeta; + /// Attempt classic pairing with device of address `CURR_ADDRESS`. Future pair({dynamic hint}); FlutterRustBridgeTaskConstMeta get kPairConstMeta; } + +class StringArray2 extends NonGrowableListView { + static const arraySize = 2; + StringArray2(List inner) + : assert(inner.length == arraySize), + super(inner); + StringArray2.unchecked(List inner) : super(inner); + StringArray2.init(String fill) : super(List.filled(arraySize, fill)); +} diff --git a/fastpair/rust/demo/lib/bridge_generated.dart b/fastpair/rust/demo/lib/bridge_generated.dart index d098d402..77557675 100644 --- a/fastpair/rust/demo/lib/bridge_generated.dart +++ b/fastpair/rust/demo/lib/bridge_generated.dart @@ -41,10 +41,10 @@ class RustImpl implements Rust { argNames: [], ); - Stream eventStream({dynamic hint}) { + Stream eventStream({dynamic hint}) { return _platform.executeStream(FlutterRustBridgeTask( callFfi: (port_) => _platform.inner.wire_event_stream(port_), - parseSuccessData: _wire2api_String, + parseSuccessData: _wire2api_String_array_2, constMeta: kEventStreamConstMeta, argValues: [], hint: hint, @@ -82,6 +82,14 @@ class RustImpl implements Rust { return raw as String; } + StringArray2 _wire2api_String_array_2(dynamic raw) { + return StringArray2((raw as List).map(_wire2api_String).toList()); + } + + List _wire2api_list_String(dynamic raw) { + return (raw as List).map(_wire2api_String).toList(); + } + int _wire2api_u8(dynamic raw) { return raw as int; } diff --git a/fastpair/rust/demo/lib/main.dart b/fastpair/rust/demo/lib/main.dart index da0bb798..40534f31 100644 --- a/fastpair/rust/demo/lib/main.dart +++ b/fastpair/rust/demo/lib/main.dart @@ -27,16 +27,22 @@ class HomePage extends StatelessWidget { appBar: AppBar(title: const Text("Fast Pair")), body: Center( child: StreamBuilder( - // Retrieve device stream from Rust side. + // Retrieve device info stream from Rust side. stream: api.eventStream(), - builder: (context, deviceName) { - if (deviceName.hasData) { + builder: (context, deviceInfo) { + if (deviceInfo.hasData) { return Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text(deviceName.data!), + children: [ + // `deviceInfo.data[0]` holds device name. + // `deviceInfo.data[1]` holds image URL. + Expanded( + child: Image.network(deviceInfo.data![1], + fit: BoxFit.contain)), + Text(deviceInfo.data![0]), OutlinedButton( + // Invoke pairing dialog. onPressed: () => showDialog( context: context, // Rust functions are invoked as futures. diff --git a/fastpair/rust/demo/local/706908.json b/fastpair/rust/demo/local/706908.json new file mode 100644 index 00000000..3a77259c --- /dev/null +++ b/fastpair/rust/demo/local/706908.json @@ -0,0 +1,40 @@ +{ + "device": { + "id": "706908", + "notificationType": "FAST_PAIR_ONE", + "imageUrl": "https://lh3.googleusercontent.com/9lYIq9GW5_tZ1WaTYsrU7NMc5MP8AgOcsHB5K75MlfeqhwIgm4jL_ilMtP9aLYEZR_6jx4rLI2-2-uYDjg", + "name": "Sony WH-1000XM3", + "intentUri": "intent:#Intent;action=com.google.android.gms.nearby.discovery%3AACTION_MAGIC_PAIR;package=com.google.android.gms;component=com.google.android.gms/.nearby.discovery.service.DiscoveryService;end", + "triggerDistance": 0.6, + "antiSpoofingKeyPair": {}, + "status": { + "statusType": "PUBLISHED" + }, + "lastUpdateTimestamp": "2020-05-19T17:58:19.881Z", + "deviceType": "HEADPHONES", + "trueWirelessImages": {}, + "companyName": "Sony", + "interactionType": "NOTIFICATION", + "companionDetail": {} + }, + "image": "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", + "strings": { + "initialNotificationDescription": "Tap to pair with this device", + "openCompanionAppDescription": "Tap to finish setup", + "updateCompanionAppDescription": "Tap to update device settings and finish setup", + "downloadCompanionAppDescription": "Tap to download device app on Google Play and see all features", + "unableToConnectTitle": "Unable to connect", + "unableToConnectDescription": "Try manually pairing to the device", + "initialPairingDescription": "%s will appear on devices linked with %s", + "connectSuccessCompanionAppInstalled": "Your device is ready to be set up", + "connectSuccessCompanionAppNotInstalled": "Download the device app on Google Play to see all available features", + "subsequentPairingDescription": "Connect %s to this phone", + "retroactivePairingDescription": "Save device to %s to connect more quickly to your other devices", + "waitLaunchCompanionAppDescription": "This will take a few moments", + "failConnectGoToSettingsDescription": "Try manually pairing to the device by going to Settings", + "assistantSetupHalfSheet": "Get hands-free help on the go from Google Assistant", + "assistantSetupNotification": "Tap to set up your Google Assistant", + "fastPairTvConnectDeviceNoAccountDescription": "Connect your %s with this device", + "subsequentPairingDescriptionOnTv": "Connect %s to TV" + } +} diff --git a/fastpair/rust/demo/rust/src/advertisement.rs b/fastpair/rust/demo/rust/src/advertisement.rs index 46aae17b..bb3dd601 100644 --- a/fastpair/rust/demo/rust/src/advertisement.rs +++ b/fastpair/rust/demo/rust/src/advertisement.rs @@ -14,7 +14,10 @@ use bluetooth::{BleAddress, BleAdvertisement, ServiceData}; -use crate::decoder::FpDecoder; +use crate::{ + decoder::FpDecoder, + fetcher::{FpFetcher, FpFetcherLocal}, +}; /// Represents a FP device model ID. pub(crate) type ModelId = String; @@ -27,6 +30,8 @@ pub(crate) struct FpPairingAdvertisement { /// Estimated distance in meters of device from BLE adapter. distance: f64, model_id: ModelId, + name: String, + image_url: String, } impl FpPairingAdvertisement { @@ -72,10 +77,18 @@ impl FpPairingAdvertisement { model_id.insert(0, 0); let model_id = format!("{}", u32::from_be_bytes(model_id.try_into().unwrap())); + // Retrieve device info of the device corresponding to this model ID. + let fetcher = FpFetcherLocal::new(String::from("./local")); + let device_info = fetcher + .get_device_info_from_model_id(&model_id) + .expect("Failed to create device info from model ID."); + Ok(FpPairingAdvertisement { inner: adv, distance, model_id, + name: device_info.name().to_string(), + image_url: device_info.image_url().to_string(), }) } @@ -95,6 +108,14 @@ impl FpPairingAdvertisement { pub(crate) fn model_id(&self) -> &ModelId { &self.model_id } + + pub(crate) fn name(&self) -> &String { + &self.name + } + + pub(crate) fn image_url(&self) -> &String { + &self.image_url + } } /// Convert RSSI and transmit power to distance using log-distance path loss diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index 6e94476d..8f06d78c 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, sync::RwLock}; use bluetooth::{ - api::{BleAdapter, BleDevice, ClassicDevice}, + api::{BleAdapter, ClassicDevice}, BleAdvertisement, BleDataTypeId, ClassicAddress, PairingResult, Platform, ServiceData, }; use flutter_rust_bridge::StreamSink; @@ -11,7 +11,7 @@ use tracing::{info, warn}; use crate::advertisement::FpPairingAdvertisement; // Sends a device name to Flutter via `StreamSink` FFI layer. -static NAME_STREAM: RwLock>> = RwLock::new(None); +static DEVICE_STREAM: RwLock>> = RwLock::new(None); // Saves the currently displayed device's advertisement, to be used for pairing. static CURR_DEVICE_ADV: RwLock> = RwLock::new(None); @@ -19,15 +19,14 @@ static CURR_DEVICE_ADV: RwLock> = RwLock::new(Non /// Updates the device name as displayed by Flutter. #[inline] async fn update_best_device(best_adv: FpPairingAdvertisement) { - let addr = best_adv.address(); - let ble_device = Platform::new_ble_device(addr).await.unwrap(); - let name = ble_device.name().unwrap(); - - match NAME_STREAM.read().unwrap().as_ref() { + match DEVICE_STREAM.read().unwrap().as_ref() { Some(stream) => { - stream.add(name); + stream.add([ + best_adv.name().to_string(), + best_adv.image_url().to_string(), + ]); } - None => info!("Stream is None"), + None => info!("Name stream is None"), } let mut curr_adv = CURR_DEVICE_ADV.write().unwrap(); *curr_adv = Some(best_adv); @@ -129,8 +128,8 @@ pub fn init() { } /// Sets up `StreamSink` for Dart-Rust FFI. -pub fn event_stream(s: StreamSink) -> Result<(), anyhow::Error> { - let mut stream = NAME_STREAM.write().unwrap(); +pub fn event_stream(s: StreamSink<[String; 2]>) -> Result<(), anyhow::Error> { + let mut stream = DEVICE_STREAM.write().unwrap(); *stream = Some(s); Ok(()) } diff --git a/fastpair/rust/demo/rust/src/bridge_generated.rs b/fastpair/rust/demo/rust/src/bridge_generated.rs index 66635945..5d1523c6 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.rs @@ -39,7 +39,7 @@ fn wire_event_stream_impl(port_: MessagePort) { port: Some(port_), mode: FfiCallMode::Stream, }, - move || move |task_callback| event_stream(task_callback.stream_sink::<_, String>()), + move || move |task_callback| event_stream(task_callback.stream_sink::<_, [String; 2]>()), ) } fn wire_pair_impl(port_: MessagePort) { diff --git a/fastpair/rust/demo/rust/src/fetcher.rs b/fastpair/rust/demo/rust/src/fetcher.rs index 7eb9d09b..e94d49ad 100644 --- a/fastpair/rust/demo/rust/src/fetcher.rs +++ b/fastpair/rust/demo/rust/src/fetcher.rs @@ -35,19 +35,33 @@ struct JsonData { /// Types that can fetch Fast Pair data from external storage (e.g. filesystem, /// remote server). pub(crate) trait FpFetcher { - fn get_device_info_from_model_id(model_id: &ModelId) -> Result; + fn get_device_info_from_model_id( + &self, + model_id: &ModelId, + ) -> Result; } /// A unit struct for retrieving Fast Pair information from the local filesystem. -pub(crate) struct FpFetcherLocal; +pub(crate) struct FpFetcherLocal { + path: String, +} + +impl FpFetcherLocal { + pub(crate) fn new(path: String) -> Self { + FpFetcherLocal { path } + } +} impl FpFetcher for FpFetcherLocal { /// Retrieve device information for the provided Model ID. Currently, /// this information is saved locally. In the future, this should instead /// be retrieved from a remote server and cached. /// b/294456411 - fn get_device_info_from_model_id(model_id: &ModelId) -> Result { - let file_path = format!("./local/{model_id}.json"); + fn get_device_info_from_model_id( + &self, + model_id: &ModelId, + ) -> Result { + let file_path = format!("{}/{}.json", self.path, model_id); let contents = fs::read_to_string(file_path).expect("Couldn't find or open file."); let model_info: JsonData = serde_json::from_str(&contents)?; From a683ed6789f307c4f3d62354497e39ec629b2000 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 8 Aug 2023 16:01:37 -0700 Subject: [PATCH 086/128] [fp-rs] Added device dismissing functionality. The Seeker will ignore a dismissed device's advertisements until a 10s timeout expires. --- .../rust/demo/lib/bridge_definitions.dart | 6 +- fastpair/rust/demo/lib/bridge_generated.dart | 36 +++- fastpair/rust/demo/lib/main.dart | 155 ++++++++++-------- fastpair/rust/demo/rust/Cargo.toml | 1 + fastpair/rust/demo/rust/src/api.rs | 69 +++++++- .../rust/demo/rust/src/bridge_generated.io.rs | 5 + .../rust/demo/rust/src/bridge_generated.rs | 14 +- 7 files changed, 208 insertions(+), 78 deletions(-) diff --git a/fastpair/rust/demo/lib/bridge_definitions.dart b/fastpair/rust/demo/lib/bridge_definitions.dart index 5e44a0de..9c6d91bb 100644 --- a/fastpair/rust/demo/lib/bridge_definitions.dart +++ b/fastpair/rust/demo/lib/bridge_definitions.dart @@ -17,7 +17,7 @@ abstract class Rust { FlutterRustBridgeTaskConstMeta get kInitConstMeta; /// Sets up `StreamSink` for Dart-Rust FFI. - Stream eventStream({dynamic hint}); + Stream eventStream({dynamic hint}); FlutterRustBridgeTaskConstMeta get kEventStreamConstMeta; @@ -25,6 +25,10 @@ abstract class Rust { Future pair({dynamic hint}); FlutterRustBridgeTaskConstMeta get kPairConstMeta; + + Future dismiss({dynamic hint}); + + FlutterRustBridgeTaskConstMeta get kDismissConstMeta; } class StringArray2 extends NonGrowableListView { diff --git a/fastpair/rust/demo/lib/bridge_generated.dart b/fastpair/rust/demo/lib/bridge_generated.dart index 77557675..9fc5d9db 100644 --- a/fastpair/rust/demo/lib/bridge_generated.dart +++ b/fastpair/rust/demo/lib/bridge_generated.dart @@ -41,10 +41,10 @@ class RustImpl implements Rust { argNames: [], ); - Stream eventStream({dynamic hint}) { + Stream eventStream({dynamic hint}) { return _platform.executeStream(FlutterRustBridgeTask( callFfi: (port_) => _platform.inner.wire_event_stream(port_), - parseSuccessData: _wire2api_String_array_2, + parseSuccessData: _wire2api_opt_String_array_2, constMeta: kEventStreamConstMeta, argValues: [], hint: hint, @@ -73,6 +73,22 @@ class RustImpl implements Rust { argNames: [], ); + Future dismiss({dynamic hint}) { + return _platform.executeNormal(FlutterRustBridgeTask( + callFfi: (port_) => _platform.inner.wire_dismiss(port_), + parseSuccessData: _wire2api_unit, + constMeta: kDismissConstMeta, + argValues: [], + hint: hint, + )); + } + + FlutterRustBridgeTaskConstMeta get kDismissConstMeta => + const FlutterRustBridgeTaskConstMeta( + debugName: "dismiss", + argNames: [], + ); + void dispose() { _platform.dispose(); } @@ -90,6 +106,10 @@ class RustImpl implements Rust { return (raw as List).map(_wire2api_String).toList(); } + StringArray2? _wire2api_opt_String_array_2(dynamic raw) { + return raw == null ? null : _wire2api_String_array_2(raw); + } + int _wire2api_u8(dynamic raw) { return raw as int; } @@ -250,6 +270,18 @@ class RustWire implements FlutterRustBridgeWireBase { _lookup>('wire_pair'); late final _wire_pair = _wire_pairPtr.asFunction(); + void wire_dismiss( + int port_, + ) { + return _wire_dismiss( + port_, + ); + } + + late final _wire_dismissPtr = + _lookup>('wire_dismiss'); + late final _wire_dismiss = _wire_dismissPtr.asFunction(); + void free_WireSyncReturn( WireSyncReturn ptr, ) { diff --git a/fastpair/rust/demo/lib/main.dart b/fastpair/rust/demo/lib/main.dart index 40534f31..ca2565e4 100644 --- a/fastpair/rust/demo/lib/main.dart +++ b/fastpair/rust/demo/lib/main.dart @@ -8,7 +8,6 @@ void main() { class FastPairApp extends StatelessWidget { const FastPairApp({super.key}); - @override Widget build(BuildContext context) => MaterialApp( title: 'Fast Pair', @@ -24,70 +23,94 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) => Scaffold( - appBar: AppBar(title: const Text("Fast Pair")), - body: Center( - child: StreamBuilder( - // Retrieve device info stream from Rust side. - stream: api.eventStream(), - builder: (context, deviceInfo) { - if (deviceInfo.hasData) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - // `deviceInfo.data[0]` holds device name. - // `deviceInfo.data[1]` holds image URL. - Expanded( - child: Image.network(deviceInfo.data![1], - fit: BoxFit.contain)), - Text(deviceInfo.data![0]), - OutlinedButton( - // Invoke pairing dialog. - onPressed: () => showDialog( - context: context, - // Rust functions are invoked as futures. - builder: (context) => FutureBuilder( - future: api.pair(), - builder: (context, pairResult) { - return pairResult.hasData - ? AlertDialog( - title: const Text('Pairing result'), - content: Text(pairResult.data!), - actions: [ - TextButton( - onPressed: () => - Navigator.pop(context, 'OK'), - child: const Text('OK'), - ) - ], - ) - : const AlertDialog( - title: Text('Pairing...'), - // Ensures the progress indicator has sensible dimensions, - // otherwise it follows the height/width of the alert dialog. - content: Column( - mainAxisAlignment: - MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - width: 50, - height: 50, - child: - CircularProgressIndicator(), - ), - ], - ), - ); - })), - child: const Text('Pair'), - ), - ]); - } - return const Center( - child: CircularProgressIndicator(), - ); - }, + appBar: AppBar( + title: const Text("Fast Pair"), ), - )); + body: Center( + child: StreamBuilder( + // Retrieve device info stream from Rust side. + stream: api.eventStream(), + builder: (context, deviceInfo) { + var deviceName = deviceInfo.data?[0]; + var deviceImageUrl = deviceInfo.data?[1]; + + if (deviceInfo.hasData && + deviceName != null && + deviceImageUrl != null) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded( + child: Image.network(deviceImageUrl, + fit: BoxFit.contain)), + Text(deviceName), + // Spacing between device name text and buttons. + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + // Spacing between left edge of screen and first button. + const SizedBox(width: 20), + OutlinedButton( + // Invoke pairing dialog. + onPressed: () => pairing(context), + child: const Text('Pair'), + ), + // Spacing between buttons. + const SizedBox(width: 20), + OutlinedButton( + onPressed: () => api.dismiss(), + child: const Text('Dismiss')) + ], + ), + // Spacing between buttons and bottom of screen. + const SizedBox(height: 20), + ]); + } + return const Center( + child: CircularProgressIndicator(), + ); + }, + ), + ), + ); } + +// Displays pairing dialog box. +Future pairing(BuildContext context) => showDialog( + context: context, + // Rust functions are invoked as futures. + builder: (context) => FutureBuilder( + future: api.pair(), + builder: (context, pairResult) { + var pairResultValue = pairResult.data; + + return pairResult.hasData && pairResultValue != null + ? AlertDialog( + title: const Text('Pairing result'), + content: Text(pairResultValue), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context, 'OK'), + child: const Text('OK'), + ) + ], + ) + : const AlertDialog( + title: Text('Pairing...'), + // Ensures the progress indicator has sensible dimensions, + // otherwise it follows the height/width of the alert dialog. + content: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox( + width: 50, + height: 50, + child: CircularProgressIndicator(), + ), + ], + ), + ); + })); diff --git a/fastpair/rust/demo/rust/Cargo.toml b/fastpair/rust/demo/rust/Cargo.toml index 23463338..c1839a89 100644 --- a/fastpair/rust/demo/rust/Cargo.toml +++ b/fastpair/rust/demo/rust/Cargo.toml @@ -16,3 +16,4 @@ futures = { version = "0.3", features = ["executor"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tracing = "0.1.37" +ttl_cache = "0.5.1" diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index 8f06d78c..e80ae950 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, sync::RwLock}; +use std::{collections::HashMap, sync::RwLock, time::Duration}; use bluetooth::{ api::{BleAdapter, ClassicDevice}, @@ -7,24 +7,31 @@ use bluetooth::{ use flutter_rust_bridge::StreamSink; use futures::executor; use tracing::{info, warn}; +use ttl_cache::TtlCache; -use crate::advertisement::FpPairingAdvertisement; +use crate::advertisement::{FpPairingAdvertisement, ModelId}; // Sends a device name to Flutter via `StreamSink` FFI layer. -static DEVICE_STREAM: RwLock>> = RwLock::new(None); +static DEVICE_STREAM: RwLock>>> = RwLock::new(None); // Saves the currently displayed device's advertisement, to be used for pairing. static CURR_DEVICE_ADV: RwLock> = RwLock::new(None); +// Temporarily restricts which model IDs can be displayed. +static MODEL_ID_BLACKLIST: RwLock>> = RwLock::new(None); + +// How long entries should blacklisted for for. +const TTL_BLACKLIST: Duration = Duration::from_secs(10); + /// Updates the device name as displayed by Flutter. #[inline] async fn update_best_device(best_adv: FpPairingAdvertisement) { match DEVICE_STREAM.read().unwrap().as_ref() { Some(stream) => { - stream.add([ + stream.add(Some([ best_adv.name().to_string(), best_adv.image_url().to_string(), - ]); + ])); } None => info!("Name stream is None"), } @@ -62,6 +69,16 @@ fn new_best_fp_advertisement( } }; + // If blacklisted in TTL cache, skip this advertisement. + let blacklisted = match MODEL_ID_BLACKLIST.read().unwrap().as_ref() { + Some(cache) => cache.get(fp_adv.model_id()).is_some(), + None => false, + }; + if blacklisted { + latest_advertisement_map.remove(fp_adv.model_id()); + return None; + } + latest_advertisement_map.insert(fp_adv.model_id().to_owned(), fp_adv.clone()); if let Some(best_adv) = CURR_DEVICE_ADV.read().unwrap().as_ref() { @@ -94,6 +111,13 @@ fn new_best_fp_advertisement( } } +/// Sets up necessary constructs to maintain a TTL blacklist of model IDs. +#[inline] +fn init_cache() { + let mut cache = MODEL_ID_BLACKLIST.write().unwrap(); + *cache = Some(TtlCache::new(16)); +} + /// Sets up initial constructs and infinitely polls for advertisements. pub fn init() { let run = async { @@ -102,6 +126,8 @@ pub fn init() { let mut adapter = Platform::default_adapter().await.unwrap(); adapter.start_scan().unwrap(); + init_cache(); + let mut latest_advertisement_map = HashMap::new(); let datatype_selector = vec![BleDataTypeId::ServiceData16BitUuid]; @@ -128,19 +154,18 @@ pub fn init() { } /// Sets up `StreamSink` for Dart-Rust FFI. -pub fn event_stream(s: StreamSink<[String; 2]>) -> Result<(), anyhow::Error> { +pub fn event_stream(s: StreamSink>) -> Result<(), anyhow::Error> { let mut stream = DEVICE_STREAM.write().unwrap(); *stream = Some(s); Ok(()) } -/// Attempt classic pairing with device of address `CURR_ADDRESS`. +/// Attempt classic pairing with currently displayed device. pub fn pair() -> String { let result = match CURR_DEVICE_ADV.read().unwrap().as_ref() { Some(adv) => { let run = async { let classic_addr = ClassicAddress::try_from(adv.address()).unwrap(); - let classic_device = Platform::new_classic_device(classic_addr).await.unwrap(); match classic_device.pair().await { @@ -167,3 +192,31 @@ pub fn pair() -> String { info!(result); result } + +/// Remove this device from display and add it to the TTL cache blacklist. +pub fn dismiss() { + let run = async { + let mut adv = CURR_DEVICE_ADV.write().unwrap(); + match MODEL_ID_BLACKLIST.write().unwrap().as_mut() { + Some(cache) => { + let adv = adv.take(); + match adv { + Some(adv) => { + cache.insert(adv.model_id().to_string(), (), TTL_BLACKLIST); + } + None => (), + } + + match DEVICE_STREAM.read().unwrap().as_ref() { + Some(stream) => { + stream.add(None); + } + None => (), + } + } + None => (), + } + }; + + executor::block_on(run); +} diff --git a/fastpair/rust/demo/rust/src/bridge_generated.io.rs b/fastpair/rust/demo/rust/src/bridge_generated.io.rs index a3892eb9..12296aa8 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.io.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.io.rs @@ -16,6 +16,11 @@ pub extern "C" fn wire_pair(port_: i64) { wire_pair_impl(port_) } +#[no_mangle] +pub extern "C" fn wire_dismiss(port_: i64) { + wire_dismiss_impl(port_) +} + // Section: allocate functions // Section: related functions diff --git a/fastpair/rust/demo/rust/src/bridge_generated.rs b/fastpair/rust/demo/rust/src/bridge_generated.rs index 5d1523c6..8a2429f4 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.rs @@ -39,7 +39,9 @@ fn wire_event_stream_impl(port_: MessagePort) { port: Some(port_), mode: FfiCallMode::Stream, }, - move || move |task_callback| event_stream(task_callback.stream_sink::<_, [String; 2]>()), + move || { + move |task_callback| event_stream(task_callback.stream_sink::<_, Option<[String; 2]>>()) + }, ) } fn wire_pair_impl(port_: MessagePort) { @@ -52,6 +54,16 @@ fn wire_pair_impl(port_: MessagePort) { move || move |task_callback| Ok(pair()), ) } +fn wire_dismiss_impl(port_: MessagePort) { + FLUTTER_RUST_BRIDGE_HANDLER.wrap::<_, _, _, ()>( + WrapInfo { + debug_name: "dismiss", + port: Some(port_), + mode: FfiCallMode::Normal, + }, + move || move |task_callback| Ok(dismiss()), + ) +} // Section: wrapper structs // Section: static checks From b7328ddd2e685b44df71834b5c1008026e5933ed Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sun, 13 Aug 2023 17:05:10 -0700 Subject: [PATCH 087/128] [fp-rs] Adding Github actions buildrule for Rust Fast Pair and Bluetooth. --- .github/workflows/validate.yaml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index b29d16f9..24e0da2a 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -47,9 +47,11 @@ jobs: submodules: recursive - name: Build FPP run: cargo build --manifest-path presence/fpp/fpp/Cargo.toml + - name: Build Bluetooth Module + run: cargo build --manifest-path fastpair/rust/bluetooth/Cargo.toml - name: Build Fast Pair - run: cargo build --manifest-path fastpair/rust/Cargo.toml - + run: cargo build --manifest-path fastpair/rust/demo/rust/Cargo.toml + build-rust-windows: name: Build Rust on Windows runs-on: windows-latest @@ -57,6 +59,8 @@ jobs: - uses: actions/checkout@v3 with: submodules: recursive + - name: Build Bluetooth Module + run: cargo build --manifest-path fastpair/rust/bluetooth/Cargo.toml - name: Build Fast Pair - run: cargo build --manifest-path fastpair/rust/Cargo.toml + run: cargo build --manifest-path fastpair/rust/demo/rust/Cargo.toml \ No newline at end of file From 3379d0ca3e034e7d81875b7f17b298d362bac8f7 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sun, 13 Aug 2023 17:48:57 -0700 Subject: [PATCH 088/128] [fp-rs] Providing second device JSON to test multi-device setup. --- fastpair/rust/demo/local/525296.json | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 fastpair/rust/demo/local/525296.json diff --git a/fastpair/rust/demo/local/525296.json b/fastpair/rust/demo/local/525296.json new file mode 100644 index 00000000..0404cf7e --- /dev/null +++ b/fastpair/rust/demo/local/525296.json @@ -0,0 +1,40 @@ +{ + "device": { + "id": "525296", + "notificationType": "FAST_PAIR_ONE", + "imageUrl": "https://lh3.googleusercontent.com/M67kfg-lVtqeP0-CLuvR68J4MlY9wixO0Za3urah_5axGRUyi20KSEQiqjvhqxCWTxpsicU1w-TCL3BX", + "name": "LG HBS-1125", + "intentUri": "intent:#Intent;action=com.google.android.gms.nearby.discovery%3AACTION_MAGIC_PAIR;package=com.google.android.gms;component=com.google.android.gms/.nearby.discovery.service.DiscoveryService;S.com.google.android.gms.nearby.discovery%3AEXTRA_COMPANION_APP=com.lge.tonentalkplus.tonentalkfree;end", + "triggerDistance": 0.6, + "antiSpoofingKeyPair": {}, + "status": { + "statusType": "PUBLISHED" + }, + "lastUpdateTimestamp": "2021-12-02T07:11:36.110Z", + "deviceType": "HEADPHONES", + "trueWirelessImages": {}, + "companyName": "LG", + "interactionType": "NOTIFICATION", + "companionDetail": {} + }, + "image": "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", + "strings": { + "initialNotificationDescription": "Tap to pair with this device", + "openCompanionAppDescription": "Tap to finish setup", + "updateCompanionAppDescription": "Tap to update device settings and finish setup", + "downloadCompanionAppDescription": "Tap to download device app on Google Play and see all features", + "unableToConnectTitle": "Unable to connect", + "unableToConnectDescription": "Try manually pairing to the device", + "initialPairingDescription": "%s will appear on devices linked with %s", + "connectSuccessCompanionAppInstalled": "Your device is ready to be set up", + "connectSuccessCompanionAppNotInstalled": "Download the device app on Google Play to see all available features", + "subsequentPairingDescription": "Connect %s to this phone", + "retroactivePairingDescription": "Save device to %s to connect more quickly to your other devices", + "waitLaunchCompanionAppDescription": "This will take a few moments", + "failConnectGoToSettingsDescription": "Try manually pairing to the device by going to Settings", + "assistantSetupHalfSheet": "Get hands-free help on the go from Google Assistant", + "assistantSetupNotification": "Tap to set up your Google Assistant", + "fastPairTvConnectDeviceNoAccountDescription": "Connect your %s with this device", + "subsequentPairingDescriptionOnTv": "Connect %s to TV" + } +} From eb4a37084ba391b727f34983b2203d1d17760b56 Mon Sep 17 00:00:00 2001 From: hai007 Date: Mon, 14 Aug 2023 17:24:37 -0700 Subject: [PATCH 089/128] Support dynamic upgrade when the device is connected to Wifi AP after initial connection. PiperOrigin-RevId: 556959762 --- .../proto/offline_wire_formats.proto | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/connections/implementation/proto/offline_wire_formats.proto b/connections/implementation/proto/offline_wire_formats.proto index d86743f6..d0d226bd 100644 --- a/connections/implementation/proto/offline_wire_formats.proto +++ b/connections/implementation/proto/offline_wire_formats.proto @@ -47,6 +47,7 @@ message V1Frame { AUTHENTICATION_RESULT = 9; AUTO_RESUME = 10; AUTO_RECONNECT = 11; + BANDWIDTH_UPGRADE_RETRY = 12; } optional FrameType type = 1; @@ -62,6 +63,7 @@ message V1Frame { optional AuthenticationResultFrame authentication_result = 10; optional AutoResumeFrame auto_resume = 11; optional AutoReconnectFrame auto_reconnect = 12; + optional BandwidthUpgradeRetryFrame bandwidth_upgrade_retry = 13; } message ConnectionRequestFrame { @@ -307,6 +309,34 @@ message BandwidthUpgradeNegotiationFrame { optional ClientIntroductionAck client_introduction_ack = 4; } +message BandwidthUpgradeRetryFrame { + // Should always match cs/symbol:location.nearby.proto.connections.Medium + // LINT.IfChange + enum Medium { + UNKNOWN_MEDIUM = 0; + // 1 is reserved. + BLUETOOTH = 2; + WIFI_HOTSPOT = 3; + BLE = 4; + WIFI_LAN = 5; + WIFI_AWARE = 6; + NFC = 7; + WIFI_DIRECT = 8; + WEB_RTC = 9; + BLE_L2CAP = 10; + USB = 11; + } + // LINT.ThenChange(//depot/google3/third_party/nearby/proto/connections_enums.proto) + + // The mediums this device supports upgrading to. This list should be filtered + // by both the strategy and this device's individual limitations. + repeated Medium supported_medium = 1; + + // If true, expect the remote endpoint to send back the latest + // supported_medium. + optional bool is_request = 2; +} + message KeepAliveFrame { // And ack will be sent after receiving KEEP_ALIVE frame. optional bool ack = 1; From f2326f107ed419e8ed8d48896129becec057f707 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Mon, 14 Aug 2023 14:22:13 -0700 Subject: [PATCH 090/128] [fp-rs] Removing unused macOS, iOS, Android, web and Linux builds. These can be re-added in the future as needed. --- fastpair/rust/demo/android/.gitignore | 13 - fastpair/rust/demo/android/app/build.gradle | 72 -- .../android/app/src/debug/AndroidManifest.xml | 7 - .../android/app/src/main/AndroidManifest.xml | 33 - .../kotlin/com/example/demo/MainActivity.kt | 6 - .../res/drawable-v21/launch_background.xml | 12 - .../main/res/drawable/launch_background.xml | 12 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 0 bytes .../app/src/main/res/values-night/styles.xml | 18 - .../app/src/main/res/values/styles.xml | 18 - .../app/src/profile/AndroidManifest.xml | 7 - fastpair/rust/demo/android/build.gradle | 31 - fastpair/rust/demo/android/gradle.properties | 3 - .../gradle/wrapper/gradle-wrapper.properties | 5 - fastpair/rust/demo/android/settings.gradle | 11 - fastpair/rust/demo/ios/.gitignore | 34 - .../demo/ios/Flutter/AppFrameworkInfo.plist | 26 - fastpair/rust/demo/ios/Flutter/Debug.xcconfig | 1 - .../rust/demo/ios/Flutter/Release.xcconfig | 1 - .../demo/ios/Runner.xcodeproj/project.pbxproj | 613 --------------- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 98 --- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../rust/demo/ios/Runner/AppDelegate.swift | 13 - .../AppIcon.appiconset/Contents.json | 122 --- .../Icon-App-1024x1024@1x.png | Bin 10932 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 295 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 450 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 282 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 462 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 704 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 406 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 586 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 862 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 1674 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 762 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 1226 -> 0 bytes .../Icon-App-83.5x83.5@2x.png | Bin 1418 -> 0 bytes .../LaunchImage.imageset/Contents.json | 23 - .../LaunchImage.imageset/LaunchImage.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/README.md | 5 - .../Runner/Base.lproj/LaunchScreen.storyboard | 37 - .../ios/Runner/Base.lproj/Main.storyboard | 26 - fastpair/rust/demo/ios/Runner/Info.plist | 51 -- .../demo/ios/Runner/Runner-Bridging-Header.h | 1 - .../demo/ios/RunnerTests/RunnerTests.swift | 12 - fastpair/rust/demo/linux/.gitignore | 1 - fastpair/rust/demo/linux/CMakeLists.txt | 139 ---- .../rust/demo/linux/flutter/CMakeLists.txt | 88 --- .../flutter/generated_plugin_registrant.cc | 11 - .../flutter/generated_plugin_registrant.h | 15 - .../linux/flutter/generated_plugins.cmake | 23 - fastpair/rust/demo/linux/main.cc | 6 - fastpair/rust/demo/linux/my_application.cc | 104 --- fastpair/rust/demo/linux/my_application.h | 18 - fastpair/rust/demo/macos/.gitignore | 7 - .../demo/macos/Flutter/Flutter-Debug.xcconfig | 1 - .../macos/Flutter/Flutter-Release.xcconfig | 1 - .../Flutter/GeneratedPluginRegistrant.swift | 10 - .../macos/Runner.xcodeproj/project.pbxproj | 695 ------------------ .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 98 --- .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../rust/demo/macos/Runner/AppDelegate.swift | 9 - .../AppIcon.appiconset/Contents.json | 68 -- .../AppIcon.appiconset/app_icon_1024.png | Bin 102994 -> 0 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 5680 -> 0 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 520 -> 0 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 14142 -> 0 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 1066 -> 0 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 36406 -> 0 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 2218 -> 0 bytes .../demo/macos/Runner/Base.lproj/MainMenu.xib | 343 --------- .../macos/Runner/Configs/AppInfo.xcconfig | 14 - .../demo/macos/Runner/Configs/Debug.xcconfig | 2 - .../macos/Runner/Configs/Release.xcconfig | 2 - .../macos/Runner/Configs/Warnings.xcconfig | 13 - .../macos/Runner/DebugProfile.entitlements | 12 - fastpair/rust/demo/macos/Runner/Info.plist | 32 - .../demo/macos/Runner/MainFlutterWindow.swift | 15 - .../demo/macos/Runner/Release.entitlements | 8 - .../demo/macos/RunnerTests/RunnerTests.swift | 12 - fastpair/rust/demo/web/favicon.png | Bin 917 -> 0 bytes fastpair/rust/demo/web/icons/Icon-192.png | Bin 5292 -> 0 bytes fastpair/rust/demo/web/icons/Icon-512.png | Bin 8252 -> 0 bytes .../rust/demo/web/icons/Icon-maskable-192.png | Bin 5594 -> 0 bytes .../rust/demo/web/icons/Icon-maskable-512.png | Bin 20998 -> 0 bytes fastpair/rust/demo/web/index.html | 59 -- fastpair/rust/demo/web/manifest.json | 35 - 102 files changed, 3221 deletions(-) delete mode 100644 fastpair/rust/demo/android/.gitignore delete mode 100644 fastpair/rust/demo/android/app/build.gradle delete mode 100644 fastpair/rust/demo/android/app/src/debug/AndroidManifest.xml delete mode 100644 fastpair/rust/demo/android/app/src/main/AndroidManifest.xml delete mode 100644 fastpair/rust/demo/android/app/src/main/kotlin/com/example/demo/MainActivity.kt delete mode 100644 fastpair/rust/demo/android/app/src/main/res/drawable-v21/launch_background.xml delete mode 100644 fastpair/rust/demo/android/app/src/main/res/drawable/launch_background.xml delete mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 fastpair/rust/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 fastpair/rust/demo/android/app/src/main/res/values-night/styles.xml delete mode 100644 fastpair/rust/demo/android/app/src/main/res/values/styles.xml delete mode 100644 fastpair/rust/demo/android/app/src/profile/AndroidManifest.xml delete mode 100644 fastpair/rust/demo/android/build.gradle delete mode 100644 fastpair/rust/demo/android/gradle.properties delete mode 100644 fastpair/rust/demo/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 fastpair/rust/demo/android/settings.gradle delete mode 100644 fastpair/rust/demo/ios/.gitignore delete mode 100644 fastpair/rust/demo/ios/Flutter/AppFrameworkInfo.plist delete mode 100644 fastpair/rust/demo/ios/Flutter/Debug.xcconfig delete mode 100644 fastpair/rust/demo/ios/Flutter/Release.xcconfig delete mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/project.pbxproj delete mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 fastpair/rust/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 fastpair/rust/demo/ios/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 fastpair/rust/demo/ios/Runner/AppDelegate.swift delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png delete mode 100644 fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md delete mode 100644 fastpair/rust/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard delete mode 100644 fastpair/rust/demo/ios/Runner/Base.lproj/Main.storyboard delete mode 100644 fastpair/rust/demo/ios/Runner/Info.plist delete mode 100644 fastpair/rust/demo/ios/Runner/Runner-Bridging-Header.h delete mode 100644 fastpair/rust/demo/ios/RunnerTests/RunnerTests.swift delete mode 100644 fastpair/rust/demo/linux/.gitignore delete mode 100644 fastpair/rust/demo/linux/CMakeLists.txt delete mode 100644 fastpair/rust/demo/linux/flutter/CMakeLists.txt delete mode 100644 fastpair/rust/demo/linux/flutter/generated_plugin_registrant.cc delete mode 100644 fastpair/rust/demo/linux/flutter/generated_plugin_registrant.h delete mode 100644 fastpair/rust/demo/linux/flutter/generated_plugins.cmake delete mode 100644 fastpair/rust/demo/linux/main.cc delete mode 100644 fastpair/rust/demo/linux/my_application.cc delete mode 100644 fastpair/rust/demo/linux/my_application.h delete mode 100644 fastpair/rust/demo/macos/.gitignore delete mode 100644 fastpair/rust/demo/macos/Flutter/Flutter-Debug.xcconfig delete mode 100644 fastpair/rust/demo/macos/Flutter/Flutter-Release.xcconfig delete mode 100644 fastpair/rust/demo/macos/Flutter/GeneratedPluginRegistrant.swift delete mode 100644 fastpair/rust/demo/macos/Runner.xcodeproj/project.pbxproj delete mode 100644 fastpair/rust/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 fastpair/rust/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 fastpair/rust/demo/macos/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 fastpair/rust/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 fastpair/rust/demo/macos/Runner/AppDelegate.swift delete mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png delete mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png delete mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png delete mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png delete mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png delete mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png delete mode 100644 fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png delete mode 100644 fastpair/rust/demo/macos/Runner/Base.lproj/MainMenu.xib delete mode 100644 fastpair/rust/demo/macos/Runner/Configs/AppInfo.xcconfig delete mode 100644 fastpair/rust/demo/macos/Runner/Configs/Debug.xcconfig delete mode 100644 fastpair/rust/demo/macos/Runner/Configs/Release.xcconfig delete mode 100644 fastpair/rust/demo/macos/Runner/Configs/Warnings.xcconfig delete mode 100644 fastpair/rust/demo/macos/Runner/DebugProfile.entitlements delete mode 100644 fastpair/rust/demo/macos/Runner/Info.plist delete mode 100644 fastpair/rust/demo/macos/Runner/MainFlutterWindow.swift delete mode 100644 fastpair/rust/demo/macos/Runner/Release.entitlements delete mode 100644 fastpair/rust/demo/macos/RunnerTests/RunnerTests.swift delete mode 100644 fastpair/rust/demo/web/favicon.png delete mode 100644 fastpair/rust/demo/web/icons/Icon-192.png delete mode 100644 fastpair/rust/demo/web/icons/Icon-512.png delete mode 100644 fastpair/rust/demo/web/icons/Icon-maskable-192.png delete mode 100644 fastpair/rust/demo/web/icons/Icon-maskable-512.png delete mode 100644 fastpair/rust/demo/web/index.html delete mode 100644 fastpair/rust/demo/web/manifest.json diff --git a/fastpair/rust/demo/android/.gitignore b/fastpair/rust/demo/android/.gitignore deleted file mode 100644 index 6f568019..00000000 --- a/fastpair/rust/demo/android/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties -**/*.keystore -**/*.jks diff --git a/fastpair/rust/demo/android/app/build.gradle b/fastpair/rust/demo/android/app/build.gradle deleted file mode 100644 index 96442f97..00000000 --- a/fastpair/rust/demo/android/app/build.gradle +++ /dev/null @@ -1,72 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - namespace "com.example.demo" - compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.demo" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/fastpair/rust/demo/android/app/src/debug/AndroidManifest.xml b/fastpair/rust/demo/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 399f6981..00000000 --- a/fastpair/rust/demo/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/fastpair/rust/demo/android/app/src/main/AndroidManifest.xml b/fastpair/rust/demo/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index 81300528..00000000 --- a/fastpair/rust/demo/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - diff --git a/fastpair/rust/demo/android/app/src/main/kotlin/com/example/demo/MainActivity.kt b/fastpair/rust/demo/android/app/src/main/kotlin/com/example/demo/MainActivity.kt deleted file mode 100644 index 34b9c4c6..00000000 --- a/fastpair/rust/demo/android/app/src/main/kotlin/com/example/demo/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.demo - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/fastpair/rust/demo/android/app/src/main/res/drawable-v21/launch_background.xml b/fastpair/rust/demo/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index f74085f3..00000000 --- a/fastpair/rust/demo/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/fastpair/rust/demo/android/app/src/main/res/drawable/launch_background.xml b/fastpair/rust/demo/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f8..00000000 --- a/fastpair/rust/demo/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/fastpair/rust/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/fastpair/rust/demo/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/fastpair/rust/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/fastpair/rust/demo/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/fastpair/rust/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/fastpair/rust/demo/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/fastpair/rust/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/fastpair/rust/demo/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/fastpair/rust/demo/android/app/src/main/res/values-night/styles.xml b/fastpair/rust/demo/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 06952be7..00000000 --- a/fastpair/rust/demo/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/fastpair/rust/demo/android/app/src/main/res/values/styles.xml b/fastpair/rust/demo/android/app/src/main/res/values/styles.xml deleted file mode 100644 index cb1ef880..00000000 --- a/fastpair/rust/demo/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/fastpair/rust/demo/android/app/src/profile/AndroidManifest.xml b/fastpair/rust/demo/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 399f6981..00000000 --- a/fastpair/rust/demo/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/fastpair/rust/demo/android/build.gradle b/fastpair/rust/demo/android/build.gradle deleted file mode 100644 index f7eb7f63..00000000 --- a/fastpair/rust/demo/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/fastpair/rust/demo/android/gradle.properties b/fastpair/rust/demo/android/gradle.properties deleted file mode 100644 index 94adc3a3..00000000 --- a/fastpair/rust/demo/android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true diff --git a/fastpair/rust/demo/android/gradle/wrapper/gradle-wrapper.properties b/fastpair/rust/demo/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 3c472b99..00000000 --- a/fastpair/rust/demo/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/fastpair/rust/demo/android/settings.gradle b/fastpair/rust/demo/android/settings.gradle deleted file mode 100644 index 44e62bcf..00000000 --- a/fastpair/rust/demo/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/fastpair/rust/demo/ios/.gitignore b/fastpair/rust/demo/ios/.gitignore deleted file mode 100644 index 7a7f9873..00000000 --- a/fastpair/rust/demo/ios/.gitignore +++ /dev/null @@ -1,34 +0,0 @@ -**/dgph -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/ephemeral/ -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/fastpair/rust/demo/ios/Flutter/AppFrameworkInfo.plist b/fastpair/rust/demo/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 9625e105..00000000 --- a/fastpair/rust/demo/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 11.0 - - diff --git a/fastpair/rust/demo/ios/Flutter/Debug.xcconfig b/fastpair/rust/demo/ios/Flutter/Debug.xcconfig deleted file mode 100644 index 592ceee8..00000000 --- a/fastpair/rust/demo/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/fastpair/rust/demo/ios/Flutter/Release.xcconfig b/fastpair/rust/demo/ios/Flutter/Release.xcconfig deleted file mode 100644 index 592ceee8..00000000 --- a/fastpair/rust/demo/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "Generated.xcconfig" diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/project.pbxproj b/fastpair/rust/demo/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 660a2ade..00000000 --- a/fastpair/rust/demo/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,613 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 97C146E61CF9000F007C117D /* Project object */; - proxyType = 1; - remoteGlobalIDString = 97C146ED1CF9000F007C117D; - remoteInfo = Runner; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 331C8082294A63A400263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C807B294A618700263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - 331C8082294A63A400263BE5 /* RunnerTests */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - 331C8081294A63A400263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C8080294A63A400263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 331C807D294A63A400263BE5 /* Sources */, - 331C807E294A63A400263BE5 /* Frameworks */, - 331C807F294A63A400263BE5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 331C8086294A63A400263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C8080294A63A400263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 97C146ED1CF9000F007C117D; - }; - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - 331C8080294A63A400263BE5 /* RunnerTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C807F294A63A400263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C807D294A63A400263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 97C146ED1CF9000F007C117D /* Runner */; - targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.demo; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 331C8088294A63A400263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = AE0B7B92F70575B8D7E0D07E /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Debug; - }; - 331C8089294A63A400263BE5 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 89B67EB44CE7B6631473024E /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Release; - }; - 331C808A294A63A400263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 640959BDD8F10B91D80A66BE /* Pods-RunnerTests.profile.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.demo; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.demo; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C8088294A63A400263BE5 /* Debug */, - 331C8089294A63A400263BE5 /* Release */, - 331C808A294A63A400263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a6..00000000 --- a/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5..00000000 --- a/fastpair/rust/demo/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/fastpair/rust/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/fastpair/rust/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index e42adcb3..00000000 --- a/fastpair/rust/demo/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fastpair/rust/demo/ios/Runner.xcworkspace/contents.xcworkspacedata b/fastpair/rust/demo/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16..00000000 --- a/fastpair/rust/demo/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5..00000000 --- a/fastpair/rust/demo/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/fastpair/rust/demo/ios/Runner/AppDelegate.swift b/fastpair/rust/demo/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4a..00000000 --- a/fastpair/rust/demo/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab..00000000 --- a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725e9b0ddb1deab583e5b5102493aa332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10932 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 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png deleted file mode 100644 index 797d452e458972bab9d994556c8305db4c827017..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index 6ed2d933e1120817fe9182483a228007b18ab6ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png deleted file mode 100644 index 4cd7b0099ca80c806f8fe495613e8d6c69460d76..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index fe730945a01f64a61e2235dbe3f45b08f7729182..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index 502f463a9bc882b461c96aadf492d1729e49e725..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index 0ec303439225b78712f49115768196d8d76f6790..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index e9f5fea27c705180eb716271f41b582e76dcbd90..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1674 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 diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png deleted file mode 100644 index 0467bf12aa4d28f374bb26596605a46dcbb3e7c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json deleted file mode 100644 index 0bedcf2f..00000000 --- a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "LaunchImage.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "LaunchImage@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b..00000000 --- a/fastpair/rust/demo/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/fastpair/rust/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard b/fastpair/rust/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7..00000000 --- a/fastpair/rust/demo/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fastpair/rust/demo/ios/Runner/Base.lproj/Main.storyboard b/fastpair/rust/demo/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516..00000000 --- a/fastpair/rust/demo/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fastpair/rust/demo/ios/Runner/Info.plist b/fastpair/rust/demo/ios/Runner/Info.plist deleted file mode 100644 index 5d784104..00000000 --- a/fastpair/rust/demo/ios/Runner/Info.plist +++ /dev/null @@ -1,51 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Demo - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - demo - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - CADisableMinimumFrameDurationOnPhone - - UIApplicationSupportsIndirectInputEvents - - - diff --git a/fastpair/rust/demo/ios/Runner/Runner-Bridging-Header.h b/fastpair/rust/demo/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a56..00000000 --- a/fastpair/rust/demo/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/fastpair/rust/demo/ios/RunnerTests/RunnerTests.swift b/fastpair/rust/demo/ios/RunnerTests/RunnerTests.swift deleted file mode 100644 index 86a7c3b1..00000000 --- a/fastpair/rust/demo/ios/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Flutter -import UIKit -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/fastpair/rust/demo/linux/.gitignore b/fastpair/rust/demo/linux/.gitignore deleted file mode 100644 index d3896c98..00000000 --- a/fastpair/rust/demo/linux/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flutter/ephemeral diff --git a/fastpair/rust/demo/linux/CMakeLists.txt b/fastpair/rust/demo/linux/CMakeLists.txt deleted file mode 100644 index d8d150ac..00000000 --- a/fastpair/rust/demo/linux/CMakeLists.txt +++ /dev/null @@ -1,139 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.10) -project(runner LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "demo") -# The unique GTK application identifier for this application. See: -# https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.demo") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0063 NEW) - -# Load bundled libraries from the lib/ directory relative to the binary. -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Root filesystem for cross-building. -if(FLUTTER_TARGET_PLATFORM_SYSROOT) - set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endif() - -# Define build configuration options. -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") -endif() - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_14) - target_compile_options(${TARGET} PRIVATE -Wall -Werror) - target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") - target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) - -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Define the application target. To change its name, change BINARY_NAME above, -# not the value here, or `flutter run` will no longer work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add dependency libraries. Add any application-specific dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) - -# Only the install-generated bundle's copy of the executable will launch -# correctly, since the resources must in the right relative locations. To avoid -# people trying to run the unbundled copy, put it in a subdirectory instead of -# the default top-level location. -set_target_properties(${BINARY_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" -) - - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# By default, "installing" just makes a relocatable bundle in the build -# directory. -set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -# Start with a clean build bundle directory every time. -install(CODE " - file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") - " COMPONENT Runtime) - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) - install(FILES "${bundled_library}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endforeach(bundled_library) - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") - install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() diff --git a/fastpair/rust/demo/linux/flutter/CMakeLists.txt b/fastpair/rust/demo/linux/flutter/CMakeLists.txt deleted file mode 100644 index d5bd0164..00000000 --- a/fastpair/rust/demo/linux/flutter/CMakeLists.txt +++ /dev/null @@ -1,88 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.10) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. - -# Serves the same purpose as list(TRANSFORM ... PREPEND ...), -# which isn't available in 3.10. -function(list_prepend LIST_NAME PREFIX) - set(NEW_LIST "") - foreach(element ${${LIST_NAME}}) - list(APPEND NEW_LIST "${PREFIX}${element}") - endforeach(element) - set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) -endfunction() - -# === Flutter Library === -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) -pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) - -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "fl_basic_message_channel.h" - "fl_binary_codec.h" - "fl_binary_messenger.h" - "fl_dart_project.h" - "fl_engine.h" - "fl_json_message_codec.h" - "fl_json_method_codec.h" - "fl_message_codec.h" - "fl_method_call.h" - "fl_method_channel.h" - "fl_method_codec.h" - "fl_method_response.h" - "fl_plugin_registrar.h" - "fl_plugin_registry.h" - "fl_standard_message_codec.h" - "fl_standard_method_codec.h" - "fl_string_codec.h" - "fl_value.h" - "fl_view.h" - "flutter_linux.h" -) -list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") -target_link_libraries(flutter INTERFACE - PkgConfig::GTK - PkgConfig::GLIB - PkgConfig::GIO -) -add_dependencies(flutter flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CMAKE_CURRENT_BINARY_DIR}/_phony_ - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} -) diff --git a/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.cc b/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index e71a16d2..00000000 --- a/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,11 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - - -void fl_register_plugins(FlPluginRegistry* registry) { -} diff --git a/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.h b/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47b..00000000 --- a/fastpair/rust/demo/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/fastpair/rust/demo/linux/flutter/generated_plugins.cmake b/fastpair/rust/demo/linux/flutter/generated_plugins.cmake deleted file mode 100644 index 2e1de87a..00000000 --- a/fastpair/rust/demo/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,23 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/fastpair/rust/demo/linux/main.cc b/fastpair/rust/demo/linux/main.cc deleted file mode 100644 index e7c5c543..00000000 --- a/fastpair/rust/demo/linux/main.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include "my_application.h" - -int main(int argc, char** argv) { - g_autoptr(MyApplication) app = my_application_new(); - return g_application_run(G_APPLICATION(app), argc, argv); -} diff --git a/fastpair/rust/demo/linux/my_application.cc b/fastpair/rust/demo/linux/my_application.cc deleted file mode 100644 index 0d6f1cce..00000000 --- a/fastpair/rust/demo/linux/my_application.cc +++ /dev/null @@ -1,104 +0,0 @@ -#include "my_application.h" - -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif - -#include "flutter/generated_plugin_registrant.h" - -struct _MyApplication { - GtkApplication parent_instance; - char** dart_entrypoint_arguments; -}; - -G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - MyApplication* self = MY_APPLICATION(application); - GtkWindow* window = - GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); - - // Use a header bar when running in GNOME as this is the common style used - // by applications and is the setup most users will be using (e.g. Ubuntu - // desktop). - // If running on X and not using GNOME then just use a traditional title bar - // in case the window manager does more exotic layout, e.g. tiling. - // If running on Wayland assume the header bar will work (may need changing - // if future cases occur). - gboolean use_header_bar = TRUE; -#ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { - const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); - if (g_strcmp0(wm_name, "GNOME Shell") != 0) { - use_header_bar = FALSE; - } - } -#endif - if (use_header_bar) { - GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); - gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "demo"); - gtk_header_bar_set_show_close_button(header_bar, TRUE); - gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); - } else { - gtk_window_set_title(window, "demo"); - } - - gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); - - g_autoptr(FlDartProject) project = fl_dart_project_new(); - fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); - - FlView* view = fl_view_new(project); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -// Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { - MyApplication* self = MY_APPLICATION(application); - // Strip out the first argument as it is the binary name. - self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); - - g_autoptr(GError) error = nullptr; - if (!g_application_register(application, nullptr, &error)) { - g_warning("Failed to register: %s", error->message); - *exit_status = 1; - return TRUE; - } - - g_application_activate(application); - *exit_status = 0; - - return TRUE; -} - -// Implements GObject::dispose. -static void my_application_dispose(GObject* object) { - MyApplication* self = MY_APPLICATION(object); - g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); - G_OBJECT_CLASS(my_application_parent_class)->dispose(object); -} - -static void my_application_class_init(MyApplicationClass* klass) { - G_APPLICATION_CLASS(klass)->activate = my_application_activate; - G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; - G_OBJECT_CLASS(klass)->dispose = my_application_dispose; -} - -static void my_application_init(MyApplication* self) {} - -MyApplication* my_application_new() { - return MY_APPLICATION(g_object_new(my_application_get_type(), - "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, - nullptr)); -} diff --git a/fastpair/rust/demo/linux/my_application.h b/fastpair/rust/demo/linux/my_application.h deleted file mode 100644 index 72271d5e..00000000 --- a/fastpair/rust/demo/linux/my_application.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FLUTTER_MY_APPLICATION_H_ -#define FLUTTER_MY_APPLICATION_H_ - -#include - -G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, - GtkApplication) - -/** - * my_application_new: - * - * Creates a new Flutter-based application. - * - * Returns: a new #MyApplication. - */ -MyApplication* my_application_new(); - -#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/fastpair/rust/demo/macos/.gitignore b/fastpair/rust/demo/macos/.gitignore deleted file mode 100644 index 746adbb6..00000000 --- a/fastpair/rust/demo/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/fastpair/rust/demo/macos/Flutter/Flutter-Debug.xcconfig b/fastpair/rust/demo/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index c2efd0b6..00000000 --- a/fastpair/rust/demo/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/fastpair/rust/demo/macos/Flutter/Flutter-Release.xcconfig b/fastpair/rust/demo/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index c2efd0b6..00000000 --- a/fastpair/rust/demo/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1 +0,0 @@ -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/fastpair/rust/demo/macos/Flutter/GeneratedPluginRegistrant.swift b/fastpair/rust/demo/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index cccf817a..00000000 --- a/fastpair/rust/demo/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { -} diff --git a/fastpair/rust/demo/macos/Runner.xcodeproj/project.pbxproj b/fastpair/rust/demo/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index de4d6636..00000000 --- a/fastpair/rust/demo/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,695 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC10EC2044A3C60003C045; - remoteInfo = Runner; - }; - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "demo.app"; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 331C80D2294CF70F00263BE5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 331C80D6294CF71000263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C80D7294CF71000263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 331C80D6294CF71000263BE5 /* RunnerTests */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* demo.app */, - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C80D4294CF70F00263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 331C80D1294CF70F00263BE5 /* Sources */, - 331C80D2294CF70F00263BE5 /* Frameworks */, - 331C80D3294CF70F00263BE5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 331C80DA294CF71000263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* demo.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C80D4294CF70F00263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 33CC10EC2044A3C60003C045; - }; - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 331C80D4294CF70F00263BE5 /* RunnerTests */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C80D3294CF70F00263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C80D1294CF70F00263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC10EC2044A3C60003C045 /* Runner */; - targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; - }; - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 331C80DB294CF71000263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/demo"; - }; - name = Debug; - }; - 331C80DC294CF71000263BE5 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/demo"; - }; - name = Release; - }; - 331C80DD294CF71000263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.demo.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/demo"; - }; - name = Profile; - }; - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C80DB294CF71000263BE5 /* Debug */, - 331C80DC294CF71000263BE5 /* Release */, - 331C80DD294CF71000263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/fastpair/rust/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/fastpair/rust/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/fastpair/rust/demo/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/fastpair/rust/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/fastpair/rust/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 0b1d244f..00000000 --- a/fastpair/rust/demo/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fastpair/rust/demo/macos/Runner.xcworkspace/contents.xcworkspacedata b/fastpair/rust/demo/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16..00000000 --- a/fastpair/rust/demo/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/fastpair/rust/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/fastpair/rust/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/fastpair/rust/demo/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/fastpair/rust/demo/macos/Runner/AppDelegate.swift b/fastpair/rust/demo/macos/Runner/AppDelegate.swift deleted file mode 100644 index d53ef643..00000000 --- a/fastpair/rust/demo/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f1..00000000 --- a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 82b6f9d9a33e198f5747104729e1fcef999772a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 13b35eba55c6dabc3aac36f33d859266c18fa0d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5680 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 diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 0a3f5fa40fb3d1e0710331a48de5d256da3f275d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 520 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 diff --git a/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/fastpair/rust/demo/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 2f1632cfddf3d9dade342351e627a0a75609fb46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fastpair/rust/demo/macos/Runner/Configs/AppInfo.xcconfig b/fastpair/rust/demo/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index 6ad99cad..00000000 --- a/fastpair/rust/demo/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = demo - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.demo - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. diff --git a/fastpair/rust/demo/macos/Runner/Configs/Debug.xcconfig b/fastpair/rust/demo/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd94..00000000 --- a/fastpair/rust/demo/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/fastpair/rust/demo/macos/Runner/Configs/Release.xcconfig b/fastpair/rust/demo/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f495..00000000 --- a/fastpair/rust/demo/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/fastpair/rust/demo/macos/Runner/Configs/Warnings.xcconfig b/fastpair/rust/demo/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf47..00000000 --- a/fastpair/rust/demo/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/fastpair/rust/demo/macos/Runner/DebugProfile.entitlements b/fastpair/rust/demo/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index dddb8a30..00000000 --- a/fastpair/rust/demo/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/fastpair/rust/demo/macos/Runner/Info.plist b/fastpair/rust/demo/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6..00000000 --- a/fastpair/rust/demo/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/fastpair/rust/demo/macos/Runner/MainFlutterWindow.swift b/fastpair/rust/demo/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 3cc05eb2..00000000 --- a/fastpair/rust/demo/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/fastpair/rust/demo/macos/Runner/Release.entitlements b/fastpair/rust/demo/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a4..00000000 --- a/fastpair/rust/demo/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/fastpair/rust/demo/macos/RunnerTests/RunnerTests.swift b/fastpair/rust/demo/macos/RunnerTests/RunnerTests.swift deleted file mode 100644 index 5418c9f5..00000000 --- a/fastpair/rust/demo/macos/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import FlutterMacOS -import Cocoa -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/fastpair/rust/demo/web/favicon.png b/fastpair/rust/demo/web/favicon.png deleted file mode 100644 index 8aaa46ac1ae21512746f852a42ba87e4165dfdd1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM diff --git a/fastpair/rust/demo/web/icons/Icon-192.png b/fastpair/rust/demo/web/icons/Icon-192.png deleted file mode 100644 index b749bfef07473333cf1dd31e9eed89862a5d52aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5292 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 diff --git a/fastpair/rust/demo/web/icons/Icon-512.png b/fastpair/rust/demo/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff1169879ba46840804b412fe02fefd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8252 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 diff --git a/fastpair/rust/demo/web/icons/Icon-maskable-192.png b/fastpair/rust/demo/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d76e525556d5d89141648c724331630325d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! diff --git a/fastpair/rust/demo/web/icons/Icon-maskable-512.png b/fastpair/rust/demo/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c56691fbdb0b7efa65097c7cc1edac12a6d3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20998 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 MPgg&ebxsLQ025IeI{*Lx diff --git a/fastpair/rust/demo/web/index.html b/fastpair/rust/demo/web/index.html deleted file mode 100644 index 1a98da7d..00000000 --- a/fastpair/rust/demo/web/index.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - demo - - - - - - - - - - diff --git a/fastpair/rust/demo/web/manifest.json b/fastpair/rust/demo/web/manifest.json deleted file mode 100644 index 238a284b..00000000 --- a/fastpair/rust/demo/web/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "demo", - "short_name": "demo", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} From bf78f30792901af82b434ef9fb8e11ae0aa2ad73 Mon Sep 17 00:00:00 2001 From: Joy Babafemi Date: Tue, 15 Aug 2023 14:04:42 -0700 Subject: [PATCH 091/128] Modify UwbControleeCapabilities to support multi-chip environment. PiperOrigin-RevId: 557244065 --- presence/proto/presence_frame.proto | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/presence/proto/presence_frame.proto b/presence/proto/presence_frame.proto index 57d66071..5b4ba2ff 100644 --- a/presence/proto/presence_frame.proto +++ b/presence/proto/presence_frame.proto @@ -118,6 +118,14 @@ message UwbControleeCapabilities { [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; +} + +/* A frame containing info needed per chip in a multi-chip environment. */ +message UwbMultiChipInfo { + optional bytes controlee_address = 1; + optional string chip_id = 2; } /** From c8230a7ec3b261d8c3cb0d1bff46e1a8bf323b65 Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Tue, 15 Aug 2023 14:18:05 -0700 Subject: [PATCH 092/128] Optimize application startup time PiperOrigin-RevId: 557248394 --- internal/platform/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 66056d0f..e839fdbd 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -344,6 +344,7 @@ cc_library( "//internal/test:__subpackages__", "//internal/weave:__subpackages__", "//location/nearby/analytics/cpp:__subpackages__", + "//location/nearby/apps:__subpackages__", "//location/nearby/cpp:__subpackages__", "//location/nearby/testing/nearby_native:__subpackages__", "//presence:__subpackages__", From 41be4cca57c9a88f9d103e7b2bd40dd8ee1a7bdb Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 15 Aug 2023 14:05:42 -0700 Subject: [PATCH 093/128] [fp-rs] Adding copyright notice under windows/ and bindgen files. --- fastpair/rust/demo/bindgen.script | 3 +++ fastpair/rust/demo/lib/bridge_definitions.dart | 17 ++++++++++++++++- fastpair/rust/demo/lib/bridge_generated.dart | 14 ++++++++++++++ fastpair/rust/demo/lib/main.dart | 14 ++++++++++++++ fastpair/rust/demo/lib/rust.dart | 14 ++++++++++++++ .../rust/demo/rust/src/bridge_generated.io.rs | 14 ++++++++++++++ fastpair/rust/demo/rust/src/bridge_generated.rs | 14 ++++++++++++++ fastpair/rust/demo/rust/src/lib.rs | 14 ++++++++++++++ .../flutter/generated_plugin_registrant.cc | 14 ++++++++++++++ .../flutter/generated_plugin_registrant.h | 14 ++++++++++++++ .../rust/demo/windows/runner/flutter_window.cpp | 14 ++++++++++++++ .../rust/demo/windows/runner/flutter_window.h | 14 ++++++++++++++ fastpair/rust/demo/windows/runner/main.cpp | 14 ++++++++++++++ fastpair/rust/demo/windows/runner/resource.h | 14 ++++++++++++++ fastpair/rust/demo/windows/runner/utils.cpp | 14 ++++++++++++++ fastpair/rust/demo/windows/runner/utils.h | 14 ++++++++++++++ .../rust/demo/windows/runner/win32_window.cpp | 14 ++++++++++++++ .../rust/demo/windows/runner/win32_window.h | 14 ++++++++++++++ 18 files changed, 243 insertions(+), 1 deletion(-) diff --git a/fastpair/rust/demo/bindgen.script b/fastpair/rust/demo/bindgen.script index b8c3eb48..4533096a 100644 --- a/fastpair/rust/demo/bindgen.script +++ b/fastpair/rust/demo/bindgen.script @@ -1 +1,4 @@ flutter_rust_bridge_codegen --rust-input rust/src/api.rs --dart-output lib/bridge_generated.dart --dart-decl-output lib/bridge_definitions.dart + +# github.com/google/addlicense +addlicense . \ No newline at end of file diff --git a/fastpair/rust/demo/lib/bridge_definitions.dart b/fastpair/rust/demo/lib/bridge_definitions.dart index 9c6d91bb..67d21a9c 100644 --- a/fastpair/rust/demo/lib/bridge_definitions.dart +++ b/fastpair/rust/demo/lib/bridge_definitions.dart @@ -1,3 +1,17 @@ +// 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. + // AUTO GENERATED FILE, DO NOT EDIT. // Generated by `flutter_rust_bridge`@ 1.79.0. // ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const @@ -21,11 +35,12 @@ abstract class Rust { FlutterRustBridgeTaskConstMeta get kEventStreamConstMeta; - /// Attempt classic pairing with device of address `CURR_ADDRESS`. + /// Attempt classic pairing with currently displayed device. Future pair({dynamic hint}); FlutterRustBridgeTaskConstMeta get kPairConstMeta; + /// Remove this device from display and add it to the TTL cache blacklist. Future dismiss({dynamic hint}); FlutterRustBridgeTaskConstMeta get kDismissConstMeta; diff --git a/fastpair/rust/demo/lib/bridge_generated.dart b/fastpair/rust/demo/lib/bridge_generated.dart index 9fc5d9db..72999e9a 100644 --- a/fastpair/rust/demo/lib/bridge_generated.dart +++ b/fastpair/rust/demo/lib/bridge_generated.dart @@ -1,3 +1,17 @@ +// 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. + // AUTO GENERATED FILE, DO NOT EDIT. // Generated by `flutter_rust_bridge`@ 1.79.0. // ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const diff --git a/fastpair/rust/demo/lib/main.dart b/fastpair/rust/demo/lib/main.dart index ca2565e4..a9995d71 100644 --- a/fastpair/rust/demo/lib/main.dart +++ b/fastpair/rust/demo/lib/main.dart @@ -1,3 +1,17 @@ +// 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 'package:flutter/material.dart'; import 'package:demo/rust.dart'; diff --git a/fastpair/rust/demo/lib/rust.dart b/fastpair/rust/demo/lib/rust.dart index 9a3a86a9..a525483b 100644 --- a/fastpair/rust/demo/lib/rust.dart +++ b/fastpair/rust/demo/lib/rust.dart @@ -1,3 +1,17 @@ +// 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. + // This file initializes the dynamic library and connects it with the stub // generated by flutter_rust_bridge_codegen. diff --git a/fastpair/rust/demo/rust/src/bridge_generated.io.rs b/fastpair/rust/demo/rust/src/bridge_generated.io.rs index 12296aa8..cb0a62f4 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.io.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.io.rs @@ -1,3 +1,17 @@ +// 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. + use super::*; // Section: wire functions diff --git a/fastpair/rust/demo/rust/src/bridge_generated.rs b/fastpair/rust/demo/rust/src/bridge_generated.rs index 8a2429f4..06d8ce6e 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.rs @@ -1,3 +1,17 @@ +// 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. + #![allow( non_camel_case_types, unused, diff --git a/fastpair/rust/demo/rust/src/lib.rs b/fastpair/rust/demo/rust/src/lib.rs index 663b166e..34ba9081 100644 --- a/fastpair/rust/demo/rust/src/lib.rs +++ b/fastpair/rust/demo/rust/src/lib.rs @@ -1,3 +1,17 @@ +// 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. + mod advertisement; mod api; mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc index 8b6d4680..95e96008 100644 --- a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc @@ -1,3 +1,17 @@ +// 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. + // // Generated file. Do not edit. // diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h index dc139d85..7e32ef7b 100644 --- a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h @@ -1,3 +1,17 @@ +// 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. + // // Generated file. Do not edit. // diff --git a/fastpair/rust/demo/windows/runner/flutter_window.cpp b/fastpair/rust/demo/windows/runner/flutter_window.cpp index b25e363e..f54e1b78 100644 --- a/fastpair/rust/demo/windows/runner/flutter_window.cpp +++ b/fastpair/rust/demo/windows/runner/flutter_window.cpp @@ -1,3 +1,17 @@ +// 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 "flutter_window.h" #include diff --git a/fastpair/rust/demo/windows/runner/flutter_window.h b/fastpair/rust/demo/windows/runner/flutter_window.h index 6da0652f..da2ace7a 100644 --- a/fastpair/rust/demo/windows/runner/flutter_window.h +++ b/fastpair/rust/demo/windows/runner/flutter_window.h @@ -1,3 +1,17 @@ +// 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 RUNNER_FLUTTER_WINDOW_H_ #define RUNNER_FLUTTER_WINDOW_H_ diff --git a/fastpair/rust/demo/windows/runner/main.cpp b/fastpair/rust/demo/windows/runner/main.cpp index fec4dbaa..d88990b3 100644 --- a/fastpair/rust/demo/windows/runner/main.cpp +++ b/fastpair/rust/demo/windows/runner/main.cpp @@ -1,3 +1,17 @@ +// 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 #include #include diff --git a/fastpair/rust/demo/windows/runner/resource.h b/fastpair/rust/demo/windows/runner/resource.h index 66a65d1e..71551c8e 100644 --- a/fastpair/rust/demo/windows/runner/resource.h +++ b/fastpair/rust/demo/windows/runner/resource.h @@ -1,3 +1,17 @@ +// 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. + //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by Runner.rc diff --git a/fastpair/rust/demo/windows/runner/utils.cpp b/fastpair/rust/demo/windows/runner/utils.cpp index b2b08734..98c7118d 100644 --- a/fastpair/rust/demo/windows/runner/utils.cpp +++ b/fastpair/rust/demo/windows/runner/utils.cpp @@ -1,3 +1,17 @@ +// 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 "utils.h" #include diff --git a/fastpair/rust/demo/windows/runner/utils.h b/fastpair/rust/demo/windows/runner/utils.h index 3879d547..b089ab5f 100644 --- a/fastpair/rust/demo/windows/runner/utils.h +++ b/fastpair/rust/demo/windows/runner/utils.h @@ -1,3 +1,17 @@ +// 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 RUNNER_UTILS_H_ #define RUNNER_UTILS_H_ diff --git a/fastpair/rust/demo/windows/runner/win32_window.cpp b/fastpair/rust/demo/windows/runner/win32_window.cpp index 60608d0f..5fb3b91d 100644 --- a/fastpair/rust/demo/windows/runner/win32_window.cpp +++ b/fastpair/rust/demo/windows/runner/win32_window.cpp @@ -1,3 +1,17 @@ +// 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 "win32_window.h" #include diff --git a/fastpair/rust/demo/windows/runner/win32_window.h b/fastpair/rust/demo/windows/runner/win32_window.h index e901dde6..0597809c 100644 --- a/fastpair/rust/demo/windows/runner/win32_window.h +++ b/fastpair/rust/demo/windows/runner/win32_window.h @@ -1,3 +1,17 @@ +// 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 RUNNER_WIN32_WINDOW_H_ #define RUNNER_WIN32_WINDOW_H_ From 20db5da721580fe1c516c88ed9ebbb040c04aff5 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Tue, 15 Aug 2023 15:20:46 -0700 Subject: [PATCH 094/128] Extract SocketBase Test only change. Extracts a common base class for BluetoothSocket, BleSocket, BleV2Socket, WifiDirectSocket, WifiHotspotSocket and WifiLanSocket to reduce code duplication. PiperOrigin-RevId: 557267937 --- internal/platform/implementation/g3/BUILD | 1 + internal/platform/implementation/g3/ble.cc | 65 -------- internal/platform/implementation/g3/ble.h | 51 ++----- internal/platform/implementation/g3/ble_v2.cc | 78 +--------- internal/platform/implementation/g3/ble_v2.h | 55 ++----- .../implementation/g3/bluetooth_classic.cc | 77 +--------- .../implementation/g3/bluetooth_classic.h | 68 ++------- .../platform/implementation/g3/socket_base.h | 139 ++++++++++++++++++ .../platform/implementation/g3/wifi_direct.cc | 61 -------- .../platform/implementation/g3/wifi_direct.h | 55 ++----- .../implementation/g3/wifi_hotspot.cc | 66 --------- .../platform/implementation/g3/wifi_hotspot.h | 58 ++------ .../platform/implementation/g3/wifi_lan.cc | 65 -------- .../platform/implementation/g3/wifi_lan.h | 55 ++----- 14 files changed, 214 insertions(+), 680 deletions(-) create mode 100644 internal/platform/implementation/g3/socket_base.h diff --git a/internal/platform/implementation/g3/BUILD b/internal/platform/implementation/g3/BUILD index 5dd64cc6..02f45c6c 100644 --- a/internal/platform/implementation/g3/BUILD +++ b/internal/platform/implementation/g3/BUILD @@ -85,6 +85,7 @@ cc_library( "bluetooth_adapter.h", "bluetooth_classic.h", "credential_storage_impl.h", + "socket_base.h", "webrtc.h", "wifi.h", "wifi_direct.h", diff --git a/internal/platform/implementation/g3/ble.cc b/internal/platform/implementation/g3/ble.cc index 95bbfe47..310d19be 100644 --- a/internal/platform/implementation/g3/ble.cc +++ b/internal/platform/implementation/g3/ble.cc @@ -28,76 +28,11 @@ namespace nearby { namespace g3 { -BleSocket::~BleSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -void BleSocket::Connect(BleSocket& other) { - absl::MutexLock lock(&mutex_); - remote_socket_ = &other; - input_ = other.output_; -} - -InputStream& BleSocket::GetInputStream() { - auto* remote_socket = GetRemoteSocket(); - CHECK(remote_socket != nullptr); - return remote_socket->GetLocalInputStream(); -} - -OutputStream& BleSocket::GetOutputStream() { return GetLocalOutputStream(); } - -BleSocket* BleSocket::GetRemoteSocket() { - absl::MutexLock lock(&mutex_); - return remote_socket_; -} - -bool BleSocket::IsConnected() const { - absl::MutexLock lock(&mutex_); - return IsConnectedLocked(); -} - -bool BleSocket::IsClosed() const { - absl::MutexLock lock(&mutex_); - return closed_; -} - -Exception BleSocket::Close() { - absl::MutexLock lock(&mutex_); - DoClose(); - return {Exception::kSuccess}; -} - BlePeripheral* BleSocket::GetRemotePeripheral() { absl::MutexLock lock(&mutex_); return peripheral_; } -void BleSocket::DoClose() { - if (!closed_) { - remote_socket_ = nullptr; - output_->GetOutputStream().Close(); - output_->GetInputStream().Close(); - if (IsConnectedLocked()) { - input_->GetOutputStream().Close(); - input_->GetInputStream().Close(); - } - closed_ = true; - } -} - -bool BleSocket::IsConnectedLocked() const { return input_ != nullptr; } - -InputStream& BleSocket::GetLocalInputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetInputStream(); -} - -OutputStream& BleSocket::GetLocalOutputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetOutputStream(); -} - std::unique_ptr BleServerSocket::Accept( BlePeripheral* peripheral) { absl::MutexLock lock(&mutex_); diff --git a/internal/platform/implementation/g3/ble.h b/internal/platform/implementation/g3/ble.h index 2c8fd4c0..78736ef8 100644 --- a/internal/platform/implementation/g3/ble.h +++ b/internal/platform/implementation/g3/ble.h @@ -28,6 +28,7 @@ #include "internal/platform/implementation/g3/bluetooth_classic.h" #include "internal/platform/implementation/g3/multi_thread_executor.h" #include "internal/platform/implementation/g3/pipe.h" +#include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/input_stream.h" #include "internal/platform/output_stream.h" @@ -36,64 +37,36 @@ namespace g3 { class BleMedium; -class BleSocket : public api::BleSocket { +class BleSocket : public api::BleSocket, public SocketBase { public: BleSocket() = default; explicit BleSocket(BlePeripheral* peripheral) : peripheral_(peripheral) {} - ~BleSocket() override; - - // Connect to another BleSocket, to form a functional low-level channel. - // from this point on, and until Close is called, connection exists. - void Connect(BleSocket& other) ABSL_LOCKS_EXCLUDED(mutex_); // Returns the InputStream of this connected BleSocket. - InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + InputStream& GetInputStream() override { + return SocketBase::GetInputStream(); + } // Returns the OutputStream of this connected BleSocket. // This stream is for local side to write. - OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + OutputStream& GetOutputStream() override { + return SocketBase::GetOutputStream(); + } // Returns address of a remote BleSocket or nullptr. - BleSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if socket is closed. - bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); + BleSocket* GetRemoteSocket() { + return static_cast(SocketBase::GetRemoteSocket()); + } // Returns Exception::kIo on error, Exception::kSuccess otherwise. - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + Exception Close() override { return SocketBase::Close(); } // Returns valid BlePeripheral pointer if there is a connection, and // nullptr otherwise. BlePeripheral* GetRemotePeripheral() override ABSL_LOCKS_EXCLUDED(mutex_); private: - void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns InputStream of our side of a connection. - // This is what the remote side is supposed to read from. - // This is a helper for GetInputStream() method. - InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns OutputStream of our side of a connection. - // This is what the local size is supposed to write to. - // This is a helper for GetOutputStream() method. - OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Output pipe is initialized by constructor, it remains always valid, until - // it is closed. it represents output part of a local socket. Input part of a - // local socket comes from the peer socket, after connection. - std::shared_ptr output_{new Pipe}; - std::shared_ptr input_; - mutable absl::Mutex mutex_; BlePeripheral* peripheral_; - BleSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; }; class BleServerSocket { diff --git a/internal/platform/implementation/g3/ble_v2.cc b/internal/platform/implementation/g3/ble_v2.cc index 78886b95..f0fcf339 100644 --- a/internal/platform/implementation/g3/ble_v2.cc +++ b/internal/platform/implementation/g3/ble_v2.cc @@ -69,82 +69,14 @@ api::ble_v2::BlePeripheral::UniqueId BleV2Peripheral::GetUniqueId() const { return adapter_.GetUniqueId(); } -BleV2Socket::~BleV2Socket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -void BleV2Socket::Connect(BleV2Socket& other) { - absl::MutexLock lock(&mutex_); - remote_socket_ = &other; - input_ = other.output_; -} - -InputStream& BleV2Socket::GetInputStream() { - auto* remote_socket = GetRemoteSocket(); - CHECK(remote_socket != nullptr); - return remote_socket->GetLocalInputStream(); -} - -OutputStream& BleV2Socket::GetOutputStream() { return GetLocalOutputStream(); } - -BleV2Socket* BleV2Socket::GetRemoteSocket() { - absl::MutexLock lock(&mutex_); - return remote_socket_; -} - -bool BleV2Socket::IsConnected() const { - absl::MutexLock lock(&mutex_); - return IsConnectedLocked(); -} - -bool BleV2Socket::IsClosed() const { - absl::MutexLock lock(&mutex_); - return closed_; -} - -Exception BleV2Socket::Close() { - absl::MutexLock lock(&mutex_); - DoClose(); - return {Exception::kSuccess}; -} - BleV2Peripheral* BleV2Socket::GetRemotePeripheral() { - BluetoothAdapter* remote_adapter = nullptr; - { - absl::MutexLock lock(&mutex_); - if (remote_socket_ == nullptr || remote_socket_->adapter_ == nullptr) { - return nullptr; - } - remote_adapter = remote_socket_->adapter_; - } - if (remote_adapter == nullptr || remote_adapter->GetBleV2Medium() == nullptr) + BleV2Socket* remote_socket = GetRemoteSocket(); + if (remote_socket == nullptr || remote_socket->adapter_ == nullptr || + remote_socket->adapter_->GetBleV2Medium() == nullptr) { return nullptr; - return &(static_cast(remote_adapter->GetBleV2Medium()) - ->GetPeripheral()); -} - -void BleV2Socket::DoClose() { - if (!closed_) { - remote_socket_ = nullptr; - output_->GetOutputStream().Close(); - output_->GetInputStream().Close(); - input_->GetOutputStream().Close(); - input_->GetInputStream().Close(); - closed_ = true; } -} - -bool BleV2Socket::IsConnectedLocked() const { return input_ != nullptr; } - -InputStream& BleV2Socket::GetLocalInputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetInputStream(); -} - -OutputStream& BleV2Socket::GetLocalOutputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetOutputStream(); + return &(static_cast(remote_socket->adapter_->GetBleV2Medium()) + ->GetPeripheral()); } std::unique_ptr BleV2ServerSocket::Accept() { diff --git a/internal/platform/implementation/g3/ble_v2.h b/internal/platform/implementation/g3/ble_v2.h index 9b993086..4b273a46 100644 --- a/internal/platform/implementation/g3/ble_v2.h +++ b/internal/platform/implementation/g3/ble_v2.h @@ -30,6 +30,7 @@ #include "internal/platform/implementation/ble_v2.h" #include "internal/platform/implementation/g3/bluetooth_adapter.h" #include "internal/platform/implementation/g3/pipe.h" +#include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/medium_environment.h" #include "internal/platform/prng.h" #include "internal/platform/uuid.h" @@ -50,67 +51,35 @@ class BleV2Peripheral : public api::ble_v2::BlePeripheral { BluetoothAdapter& adapter_; }; -class BleV2Socket : public api::ble_v2::BleSocket { +class BleV2Socket : public api::ble_v2::BleSocket, public SocketBase { public: explicit BleV2Socket(BluetoothAdapter* adapter) : adapter_(adapter) {} - BleV2Socket(const BleV2Socket&) = default; - BleV2Socket& operator=(const BleV2Socket&) = default; - BleV2Socket(BleV2Socket&&) = default; - BleV2Socket& operator=(BleV2Socket&&) = default; - ~BleV2Socket() override; - - // Connect to another BleSocket, to form a functional low-level channel. - // from this point on, and until Close is called, connection exists. - void Connect(BleV2Socket& other) ABSL_LOCKS_EXCLUDED(mutex_); // Returns the InputStream of this connected BleSocket. - InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + InputStream& GetInputStream() override { + return SocketBase::GetInputStream(); + } // Returns the OutputStream of this connected BleSocket. // This stream is for local side to write. - OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + OutputStream& GetOutputStream() override { + return SocketBase::GetOutputStream(); + } // Returns address of a remote BleSocket or nullptr. - BleV2Socket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if socket is closed. - bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); + BleV2Socket* GetRemoteSocket() { + return static_cast(SocketBase::GetRemoteSocket()); + } // Returns Exception::kIo on error, Exception::kSuccess otherwise. - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + Exception Close() override { return SocketBase::Close(); } // Returns valid BlePeripheral pointer if there is a connection, and // nullptr otherwise. BleV2Peripheral* GetRemotePeripheral() override ABSL_LOCKS_EXCLUDED(mutex_); private: - void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns InputStream of our side of a connection. - // This is what the remote side is supposed to read from. - // This is a helper for GetInputStream() method. - InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns OutputStream of our side of a connection. - // This is what the local size is supposed to write to. - // This is a helper for GetOutputStream() method. - OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Output pipe is initialized by constructor, it remains always valid, until - // it is closed. it represents output part of a local socket. Input part of a - // local socket comes from the peer socket, after connection. - std::shared_ptr output_{new Pipe}; - std::shared_ptr input_; - mutable absl::Mutex mutex_; BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only. - BleV2Socket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; }; class BleV2ServerSocket : public api::ble_v2::BleServerSocket { diff --git a/internal/platform/implementation/g3/bluetooth_classic.cc b/internal/platform/implementation/g3/bluetooth_classic.cc index 58f2b7f0..d7a1a832 100644 --- a/internal/platform/implementation/g3/bluetooth_classic.cc +++ b/internal/platform/implementation/g3/bluetooth_classic.cc @@ -29,80 +29,13 @@ namespace nearby { namespace g3 { -BluetoothSocket::~BluetoothSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -void BluetoothSocket::Connect(BluetoothSocket& other) { - absl::MutexLock lock(&mutex_); - remote_socket_ = &other; - input_ = other.output_; -} - -bool BluetoothSocket::IsConnected() const { - absl::MutexLock lock(&mutex_); - return IsConnectedLocked(); -} - -bool BluetoothSocket::IsClosed() const { - absl::MutexLock lock(&mutex_); - return closed_; -} - -bool BluetoothSocket::IsConnectedLocked() const { return input_ != nullptr; } - -InputStream& BluetoothSocket::GetInputStream() { - absl::MutexLock lock(&mutex_); - if (IsConnectedLocked()) { - return input_->GetInputStream(); - } else { - return invalid_input_stream_; - } -} - -OutputStream& BluetoothSocket::GetOutputStream() { - return GetLocalOutputStream(); -} - -InputStream& BluetoothSocket::GetLocalInputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetInputStream(); -} - -OutputStream& BluetoothSocket::GetLocalOutputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetOutputStream(); -} - -Exception BluetoothSocket::Close() { - absl::MutexLock lock(&mutex_); - DoClose(); - return {Exception::kSuccess}; -} - -void BluetoothSocket::DoClose() { - if (!closed_) { - remote_socket_ = nullptr; - output_->GetOutputStream().Close(); - output_->GetInputStream().Close(); - input_->GetOutputStream().Close(); - input_->GetInputStream().Close(); - input_.reset(); - closed_ = true; - } -} - BluetoothDevice* BluetoothSocket::GetRemoteDevice() { - BluetoothAdapter* remote_adapter = nullptr; - { - absl::MutexLock lock(&mutex_); - if (remote_socket_ == nullptr || remote_socket_->adapter_ == nullptr) { - return nullptr; - } - remote_adapter = remote_socket_->adapter_; + BluetoothSocket* remote_socket = + static_cast(GetRemoteSocket()); + if (remote_socket == nullptr || remote_socket->adapter_ == nullptr) { + return nullptr; } - return remote_adapter ? &remote_adapter->GetDevice() : nullptr; + return &remote_socket->adapter_->GetDevice(); } std::unique_ptr BluetoothServerSocket::Accept() { diff --git a/internal/platform/implementation/g3/bluetooth_classic.h b/internal/platform/implementation/g3/bluetooth_classic.h index 140bdc12..6bae1fa3 100644 --- a/internal/platform/implementation/g3/bluetooth_classic.h +++ b/internal/platform/implementation/g3/bluetooth_classic.h @@ -27,6 +27,7 @@ #include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/implementation/g3/bluetooth_adapter.h" #include "internal/platform/implementation/g3/pipe.h" +#include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/input_stream.h" #include "internal/platform/listeners.h" #include "internal/platform/output_stream.h" @@ -35,83 +36,34 @@ namespace nearby { namespace g3 { // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html. -class BluetoothSocket : public api::BluetoothSocket { +class BluetoothSocket : public api::BluetoothSocket, public SocketBase { public: BluetoothSocket() = default; explicit BluetoothSocket(BluetoothAdapter* adapter) : adapter_(adapter) {} - ~BluetoothSocket() override; - - // Connects to another BluetoothSocket, to form a functional low-level - // channel. From this point on, and until Close is called, connection exists. - void Connect(BluetoothSocket& other); - - // NOTE: - // It is an undefined behavior if GetInputStream() or GetOutputStream() is - // called for a not-connected BluetoothSocket, i.e. any object that is not - // returned by BluetoothClassicMedium::ConnectToService() for client side or - // BluetoothServerSocket::Accept() for server side of connection. // Returns the InputStream of this connected BluetoothSocket. - InputStream& GetInputStream() override; + InputStream& GetInputStream() override { + return SocketBase::GetInputStream(); + } // Returns the OutputStream of this connected BluetoothSocket. // This stream is for local side to write. - OutputStream& GetOutputStream() override; - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if socket is closed. - bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); + OutputStream& GetOutputStream() override { + return SocketBase::GetOutputStream(); + } // Closes both input and output streams, marks Socket as closed. // After this call object should be treated as not connected. // Returns Exception::kIo on error, Exception::kSuccess otherwise. - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + Exception Close() override { return SocketBase::Close(); } // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice() // Returns valid BluetoothDevice pointer if there is a connection, and // nullptr otherwise. - BluetoothDevice* GetRemoteDevice() override ABSL_LOCKS_EXCLUDED(mutex_); + BluetoothDevice* GetRemoteDevice() override; private: - void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns InputStream of our side of a connection. - // This is what the remote side is supposed to read from. - // This is a helper for GetInputStream() method. - InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns OutputStream of our side of a connection. - // This is what the local size is supposed to write to. - // This is a helper for GetOutputStream() method. - OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - class InvalidInputStream : public InputStream { - public: - ExceptionOr Read(std::int64_t size) override { - return ExceptionOr(Exception::kIo); - } - ExceptionOr Skip(size_t offset) override { - return ExceptionOr(Exception::kIo); - } - Exception Close() override { return {Exception::kIo}; } - }; - // Returned to the caller if the remote socket is destroyed. - InvalidInputStream invalid_input_stream_; - - // Output pipe is initialized by constructor, it remains always valid, until - // it is closed. it represents output part of a local socket. Input part of a - // local socket comes from the peer socket, after connection. - std::shared_ptr output_{new Pipe}; - std::shared_ptr input_; - mutable absl::Mutex mutex_; BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only. - BluetoothSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; }; // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html. diff --git a/internal/platform/implementation/g3/socket_base.h b/internal/platform/implementation/g3/socket_base.h new file mode 100644 index 00000000..4da305b9 --- /dev/null +++ b/internal/platform/implementation/g3/socket_base.h @@ -0,0 +1,139 @@ +// 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_PLATFORM_IMPLEMENTATION_G3_SOCKET_BASE_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_G3_SOCKET_BASE_H_ + +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "absl/synchronization/mutex.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/g3/pipe.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/output_stream.h" + +namespace nearby { +namespace g3 { + +// Common base for BT, BLE and Wifi socket implementations. +class SocketBase { + public: + virtual ~SocketBase() { + absl::MutexLock lock(&mutex_); + DoClose(); + } + + // Connects to another Socket, to form a functional low-level + // channel. From this point on, and until Close is called, connection exists. + void Connect(SocketBase& other) ABSL_LOCKS_EXCLUDED(mutex_) { + absl::MutexLock lock(&mutex_); + remote_socket_ = &other; + input_ = other.output_; + } + + // Returns the InputStream of this connected socket. + InputStream& GetInputStream() ABSL_LOCKS_EXCLUDED(mutex_) { + absl::MutexLock lock(&mutex_); + if (IsConnectedLocked()) { + return input_->GetInputStream(); + } + return invalid_input_stream_; + } + + // Returns the OutputStream of this connected socket. + // This stream is for local side to write. + OutputStream& GetOutputStream() ABSL_LOCKS_EXCLUDED(mutex_) { + absl::MutexLock lock(&mutex_); + return output_->GetOutputStream(); + } + + // Returns true if connection exists to the (possibly closed) remote socket. + bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_) { + absl::MutexLock lock(&mutex_); + return IsConnectedLocked(); + } + + // Returns true if socket is closed. + bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_) { + absl::MutexLock lock(&mutex_); + return closed_; + } + + // Closes both input and output streams, marks Socket as closed. + // After this call object should be treated as not connected. + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + Exception Close() ABSL_LOCKS_EXCLUDED(mutex_) { + absl::MutexLock lock(&mutex_); + DoClose(); + return {Exception::kSuccess}; + } + + SocketBase* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_) { + absl::MutexLock lock(&mutex_); + return remote_socket_; + } + + protected: + mutable absl::Mutex mutex_; + + private: + void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) { + if (!closed_) { + remote_socket_ = nullptr; + output_->GetOutputStream().Close(); + output_->GetInputStream().Close(); + if (IsConnectedLocked()) { + input_->GetOutputStream().Close(); + input_->GetInputStream().Close(); + input_.reset(); + } + closed_ = true; + } + } + + // Returns true if connection exists to the (possibly closed) remote socket. + bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) { + return input_ != nullptr; + } + + class InvalidInputStream : public InputStream { + public: + ExceptionOr Read(std::int64_t size) override { + return ExceptionOr(Exception::kIo); + } + ExceptionOr Skip(size_t offset) override { + return ExceptionOr(Exception::kIo); + } + Exception Close() override { return {Exception::kIo}; } + }; + // Returned to the caller if the remote socket is destroyed. + InvalidInputStream invalid_input_stream_; + + // Output pipe is initialized by constructor, it remains always valid, until + // it is closed. it represents output part of a local socket. Input part of a + // local socket comes from the peer socket, after connection. + std::shared_ptr output_{new Pipe}; + std::shared_ptr input_; + SocketBase* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; + bool closed_ ABSL_GUARDED_BY(mutex_) = false; +}; + +} // namespace g3 +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_G3_SOCKET_BASE_H_ diff --git a/internal/platform/implementation/g3/wifi_direct.cc b/internal/platform/implementation/g3/wifi_direct.cc index e0d2cdbe..d4d97261 100644 --- a/internal/platform/implementation/g3/wifi_direct.cc +++ b/internal/platform/implementation/g3/wifi_direct.cc @@ -29,67 +29,6 @@ namespace nearby { namespace g3 { -// Code for WifiDirectSocket -WifiDirectSocket::~WifiDirectSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -void WifiDirectSocket::Connect(WifiDirectSocket& other) { - absl::MutexLock lock(&mutex_); - remote_socket_ = &other; - input_ = other.output_; -} - -InputStream& WifiDirectSocket::GetInputStream() { - auto* remote_socket = GetRemoteSocket(); - CHECK(remote_socket != nullptr); - return remote_socket->GetLocalInputStream(); -} - -OutputStream& WifiDirectSocket::GetOutputStream() { - return GetLocalOutputStream(); -} - -WifiDirectSocket* WifiDirectSocket::GetRemoteSocket() { - absl::MutexLock lock(&mutex_); - return remote_socket_; -} - -bool WifiDirectSocket::IsConnected() const { - absl::MutexLock lock(&mutex_); - return IsConnectedLocked(); -} - -Exception WifiDirectSocket::Close() { - absl::MutexLock lock(&mutex_); - DoClose(); - return {Exception::kSuccess}; -} - -void WifiDirectSocket::DoClose() { - if (!closed_) { - remote_socket_ = nullptr; - output_->GetOutputStream().Close(); - output_->GetInputStream().Close(); - input_->GetOutputStream().Close(); - input_->GetInputStream().Close(); - closed_ = true; - } -} - -bool WifiDirectSocket::IsConnectedLocked() const { return input_ != nullptr; } - -InputStream& WifiDirectSocket::GetLocalInputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetInputStream(); -} - -OutputStream& WifiDirectSocket::GetLocalOutputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetOutputStream(); -} - // Code for WifiDirectServerSocket std::string WifiDirectServerSocket::GetName(absl::string_view ip_address, int port) { diff --git a/internal/platform/implementation/g3/wifi_direct.h b/internal/platform/implementation/g3/wifi_direct.h index bab8ef25..fba77915 100644 --- a/internal/platform/implementation/g3/wifi_direct.h +++ b/internal/platform/implementation/g3/wifi_direct.h @@ -23,6 +23,7 @@ #include "absl/synchronization/mutex.h" #include "internal/platform/implementation/g3/multi_thread_executor.h" #include "internal/platform/implementation/g3/pipe.h" +#include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/implementation/wifi_direct.h" #include "internal/platform/input_stream.h" #include "internal/platform/output_stream.h" @@ -32,66 +33,28 @@ namespace g3 { class WifiDirectMedium; -class WifiDirectSocket : public api::WifiDirectSocket { +class WifiDirectSocket : public api::WifiDirectSocket, public SocketBase { public: - WifiDirectSocket() = default; - ~WifiDirectSocket() override; - WifiDirectSocket(const WifiDirectSocket&) = default; - WifiDirectSocket(WifiDirectSocket&&) = default; - WifiDirectSocket& operator=(const WifiDirectSocket&) = default; - WifiDirectSocket& operator=(WifiDirectSocket&&) = default; - - // Connect to another WifiDirectSocket, to form a functional low-level - // channel. from this point on, and until Close is called, connection exists. - void Connect(WifiDirectSocket& other) ABSL_LOCKS_EXCLUDED(mutex_); - // Returns the InputStream of the WifiDirectSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the WifiDirectSocket object is destroyed. - InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + InputStream& GetInputStream() override { + return SocketBase::GetInputStream(); + } // Returns the OutputStream of the WifiDirectSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the WifiDirectSocket object is destroyed. - OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns address of a remote WifiDirectSocket or nullptr. - WifiDirectSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_); + OutputStream& GetOutputStream() override { + return SocketBase::GetOutputStream(); + } // Returns Exception::kIo on error, Exception::kSuccess otherwise. - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - - private: - void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns InputStream of our side of a connection. - // This is what the remote side is supposed to read from. - // This is a helper for GetInputStream() method. - InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns OutputStream of our side of a connection. - // This is what the local size is supposed to write to. - // This is a helper for GetOutputStream() method. - OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Output pipe is initialized by constructor, it remains always valid, until - // it is closed. it represents output part of a local socket. Input part of a - // local socket comes from the peer socket, after connection. - std::shared_ptr output_{new Pipe}; - std::shared_ptr input_; - mutable absl::Mutex mutex_; - WifiDirectSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; + Exception Close() override { return SocketBase::Close(); } }; // WifiDirectServerSocket provides the support to server socket, this server diff --git a/internal/platform/implementation/g3/wifi_hotspot.cc b/internal/platform/implementation/g3/wifi_hotspot.cc index 49875714..d4ab753d 100644 --- a/internal/platform/implementation/g3/wifi_hotspot.cc +++ b/internal/platform/implementation/g3/wifi_hotspot.cc @@ -30,72 +30,6 @@ namespace nearby { namespace g3 { -// Code for WifiHotspotSocket -WifiHotspotSocket::~WifiHotspotSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -void WifiHotspotSocket::Connect(WifiHotspotSocket& other) { - absl::MutexLock lock(&mutex_); - remote_socket_ = &other; - input_ = other.output_; -} - -InputStream& WifiHotspotSocket::GetInputStream() { - auto* remote_socket = GetRemoteSocket(); - CHECK(remote_socket != nullptr); - return remote_socket->GetLocalInputStream(); -} - -OutputStream& WifiHotspotSocket::GetOutputStream() { - return GetLocalOutputStream(); -} - -WifiHotspotSocket* WifiHotspotSocket::GetRemoteSocket() { - absl::MutexLock lock(&mutex_); - return remote_socket_; -} - -bool WifiHotspotSocket::IsConnected() const { - absl::MutexLock lock(&mutex_); - return IsConnectedLocked(); -} - -bool WifiHotspotSocket::IsClosed() const { - absl::MutexLock lock(&mutex_); - return closed_; -} - -Exception WifiHotspotSocket::Close() { - absl::MutexLock lock(&mutex_); - DoClose(); - return {Exception::kSuccess}; -} - -void WifiHotspotSocket::DoClose() { - if (!closed_) { - remote_socket_ = nullptr; - output_->GetOutputStream().Close(); - output_->GetInputStream().Close(); - input_->GetOutputStream().Close(); - input_->GetInputStream().Close(); - closed_ = true; - } -} - -bool WifiHotspotSocket::IsConnectedLocked() const { return input_ != nullptr; } - -InputStream& WifiHotspotSocket::GetLocalInputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetInputStream(); -} - -OutputStream& WifiHotspotSocket::GetLocalOutputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetOutputStream(); -} - // Code for WifiHotspotServerSocket std::string WifiHotspotServerSocket::GetName(absl::string_view ip_address, int port) { diff --git a/internal/platform/implementation/g3/wifi_hotspot.h b/internal/platform/implementation/g3/wifi_hotspot.h index d4682621..6c248dfd 100644 --- a/internal/platform/implementation/g3/wifi_hotspot.h +++ b/internal/platform/implementation/g3/wifi_hotspot.h @@ -24,6 +24,7 @@ #include "internal/platform/byte_array.h" #include "internal/platform/implementation/g3/multi_thread_executor.h" #include "internal/platform/implementation/g3/pipe.h" +#include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/implementation/wifi_hotspot.h" #include "internal/platform/input_stream.h" #include "internal/platform/output_stream.h" @@ -33,69 +34,28 @@ namespace g3 { class WifiHotspotMedium; -class WifiHotspotSocket : public api::WifiHotspotSocket { +class WifiHotspotSocket : public api::WifiHotspotSocket, public SocketBase { public: - WifiHotspotSocket() = default; - ~WifiHotspotSocket() override; - WifiHotspotSocket(const WifiHotspotSocket&) = default; - WifiHotspotSocket(WifiHotspotSocket&&) = default; - WifiHotspotSocket& operator=(const WifiHotspotSocket&) = default; - WifiHotspotSocket& operator=(WifiHotspotSocket&&) = default; - - // Connect to another WifiHotspotSocket, to form a functional low-level - // channel. from this point on, and until Close is called, connection exists. - void Connect(WifiHotspotSocket& other) ABSL_LOCKS_EXCLUDED(mutex_); - // Returns the InputStream of the WifiHotspotSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the WifiHotspotSocket object is destroyed. - InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + InputStream& GetInputStream() override { + return SocketBase::GetInputStream(); + } // Returns the OutputStream of the WifiHotspotSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the WifiHotspotSocket object is destroyed. - OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns address of a remote WifiHotspotSocket or nullptr. - WifiHotspotSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if socket is closed. - bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); + OutputStream& GetOutputStream() override { + return SocketBase::GetOutputStream(); + } // Returns Exception::kIo on error, Exception::kSuccess otherwise. - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - - private: - void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns InputStream of our side of a connection. - // This is what the remote side is supposed to read from. - // This is a helper for GetInputStream() method. - InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns OutputStream of our side of a connection. - // This is what the local size is supposed to write to. - // This is a helper for GetOutputStream() method. - OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Output pipe is initialized by constructor, it remains always valid, until - // it is closed. it represents output part of a local socket. Input part of a - // local socket comes from the peer socket, after connection. - std::shared_ptr output_{new Pipe}; - std::shared_ptr input_; - mutable absl::Mutex mutex_; - WifiHotspotSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; + Exception Close() override { return SocketBase::Close(); } }; // WifiHotspotServerSocket provides the support to server socket, this server diff --git a/internal/platform/implementation/g3/wifi_lan.cc b/internal/platform/implementation/g3/wifi_lan.cc index 043f921f..078c67b3 100644 --- a/internal/platform/implementation/g3/wifi_lan.cc +++ b/internal/platform/implementation/g3/wifi_lan.cc @@ -31,71 +31,6 @@ namespace nearby { namespace g3 { -WifiLanSocket::~WifiLanSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -void WifiLanSocket::Connect(WifiLanSocket& other) { - absl::MutexLock lock(&mutex_); - remote_socket_ = &other; - input_ = other.output_; -} - -InputStream& WifiLanSocket::GetInputStream() { - auto* remote_socket = GetRemoteSocket(); - CHECK(remote_socket != nullptr); - return remote_socket->GetLocalInputStream(); -} - -OutputStream& WifiLanSocket::GetOutputStream() { - return GetLocalOutputStream(); -} - -WifiLanSocket* WifiLanSocket::GetRemoteSocket() { - absl::MutexLock lock(&mutex_); - return remote_socket_; -} - -bool WifiLanSocket::IsConnected() const { - absl::MutexLock lock(&mutex_); - return IsConnectedLocked(); -} - -bool WifiLanSocket::IsClosed() const { - absl::MutexLock lock(&mutex_); - return closed_; -} - -Exception WifiLanSocket::Close() { - absl::MutexLock lock(&mutex_); - DoClose(); - return {Exception::kSuccess}; -} - -void WifiLanSocket::DoClose() { - if (!closed_) { - remote_socket_ = nullptr; - output_->GetOutputStream().Close(); - output_->GetInputStream().Close(); - input_->GetOutputStream().Close(); - input_->GetInputStream().Close(); - closed_ = true; - } -} - -bool WifiLanSocket::IsConnectedLocked() const { return input_ != nullptr; } - -InputStream& WifiLanSocket::GetLocalInputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetInputStream(); -} - -OutputStream& WifiLanSocket::GetLocalOutputStream() { - absl::MutexLock lock(&mutex_); - return output_->GetOutputStream(); -} - std::string WifiLanServerSocket::GetName(const std::string& ip_address, int port) { std::string dot_delimited_string; diff --git a/internal/platform/implementation/g3/wifi_lan.h b/internal/platform/implementation/g3/wifi_lan.h index 398f2c51..19cb18d3 100644 --- a/internal/platform/implementation/g3/wifi_lan.h +++ b/internal/platform/implementation/g3/wifi_lan.h @@ -25,6 +25,7 @@ #include "internal/platform/byte_array.h" #include "internal/platform/implementation/g3/multi_thread_executor.h" #include "internal/platform/implementation/g3/pipe.h" +#include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/implementation/wifi_lan.h" #include "internal/platform/input_stream.h" #include "internal/platform/nsd_service_info.h" @@ -35,58 +36,26 @@ namespace g3 { class WifiLanMedium; -class WifiLanSocket : public api::WifiLanSocket { +class WifiLanSocket : public api::WifiLanSocket, public SocketBase { public: - WifiLanSocket() = default; - ~WifiLanSocket() override; - - // Connect to another WifiLanSocket, to form a functional low-level channel. - // from this point on, and until Close is called, connection exists. - void Connect(WifiLanSocket& other) ABSL_LOCKS_EXCLUDED(mutex_); - // Returns the InputStream of this connected WifiLanSocket. - InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + InputStream& GetInputStream() override { + return SocketBase::GetInputStream(); + } // Returns the OutputStream of this connected WifiLanSocket. // This stream is for local side to write. - OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + OutputStream& GetOutputStream() override { + return SocketBase::GetOutputStream(); + } // Returns address of a remote WifiLanSocket or nullptr. - WifiLanSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns true if socket is closed. - bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); + WifiLanSocket* GetRemoteSocket() { + return static_cast(SocketBase::GetRemoteSocket()); + } // Returns Exception::kIo on error, Exception::kSuccess otherwise. - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - - private: - void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns true if connection exists to the (possibly closed) remote socket. - bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Returns InputStream of our side of a connection. - // This is what the remote side is supposed to read from. - // This is a helper for GetInputStream() method. - InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Returns OutputStream of our side of a connection. - // This is what the local size is supposed to write to. - // This is a helper for GetOutputStream() method. - OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_); - - // Output pipe is initialized by constructor, it remains always valid, until - // it is closed. it represents output part of a local socket. Input part of a - // local socket comes from the peer socket, after connection. - std::shared_ptr output_{new Pipe}; - std::shared_ptr input_; - mutable absl::Mutex mutex_; - WifiLanSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; + Exception Close() override { return SocketBase::Close(); } }; class WifiLanServerSocket : public api::WifiLanServerSocket { From bdc109324b6ac371e407a490ac37bcbc00f37874 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 15 Aug 2023 15:21:24 -0700 Subject: [PATCH 095/128] [fp-rs] Fixing Presubmit Lint warnings for windows/ C++ files. --- .../demo/windows/flutter/generated_plugin_registrant.cc | 2 +- fastpair/rust/demo/windows/runner/flutter_window.cpp | 2 +- fastpair/rust/demo/windows/runner/flutter_window.h | 2 +- fastpair/rust/demo/windows/runner/main.cpp | 4 ++-- fastpair/rust/demo/windows/runner/resource.h | 5 ++++- fastpair/rust/demo/windows/runner/utils.cpp | 4 ++-- fastpair/rust/demo/windows/runner/win32_window.cpp | 7 ++++--- 7 files changed, 15 insertions(+), 11 deletions(-) diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc index 95e96008..5da9cfa4 100644 --- a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc @@ -18,7 +18,7 @@ // clang-format off -#include "generated_plugin_registrant.h" +#include "./generated_plugin_registrant.h" void RegisterPlugins(flutter::PluginRegistry* registry) { diff --git a/fastpair/rust/demo/windows/runner/flutter_window.cpp b/fastpair/rust/demo/windows/runner/flutter_window.cpp index f54e1b78..0f6bf6e4 100644 --- a/fastpair/rust/demo/windows/runner/flutter_window.cpp +++ b/fastpair/rust/demo/windows/runner/flutter_window.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "flutter_window.h" +#include "./flutter_window.h" #include diff --git a/fastpair/rust/demo/windows/runner/flutter_window.h b/fastpair/rust/demo/windows/runner/flutter_window.h index da2ace7a..adb318a0 100644 --- a/fastpair/rust/demo/windows/runner/flutter_window.h +++ b/fastpair/rust/demo/windows/runner/flutter_window.h @@ -20,7 +20,7 @@ #include -#include "win32_window.h" +#include "./win32_window.h" // A window that does nothing but host a Flutter view. class FlutterWindow : public Win32Window { diff --git a/fastpair/rust/demo/windows/runner/main.cpp b/fastpair/rust/demo/windows/runner/main.cpp index d88990b3..2827bb3a 100644 --- a/fastpair/rust/demo/windows/runner/main.cpp +++ b/fastpair/rust/demo/windows/runner/main.cpp @@ -16,8 +16,8 @@ #include #include -#include "flutter_window.h" -#include "utils.h" +#include "./flutter_window.h" +#include "./utils.h" int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, _In_ wchar_t *command_line, _In_ int show_command) { diff --git a/fastpair/rust/demo/windows/runner/resource.h b/fastpair/rust/demo/windows/runner/resource.h index 71551c8e..85a4c6eb 100644 --- a/fastpair/rust/demo/windows/runner/resource.h +++ b/fastpair/rust/demo/windows/runner/resource.h @@ -12,10 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -//{{NO_DEPENDENCIES}} +// {{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by Runner.rc // +#ifndef RUNNER_RESOURCE_H_ +#define RUNNER_RESOURCE_H_ #define IDI_APP_ICON 101 // Next default values for new objects @@ -28,3 +30,4 @@ #define _APS_NEXT_SYMED_VALUE 101 #endif #endif +#endif // RUNNER_RESOURCE_H_ diff --git a/fastpair/rust/demo/windows/runner/utils.cpp b/fastpair/rust/demo/windows/runner/utils.cpp index 98c7118d..070097ec 100644 --- a/fastpair/rust/demo/windows/runner/utils.cpp +++ b/fastpair/rust/demo/windows/runner/utils.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "utils.h" +#include "./utils.h" #include #include @@ -62,7 +62,7 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, nullptr, 0, nullptr, nullptr) - -1; // remove the trailing null character + -1; // remove the trailing null character int input_length = (int)wcslen(utf16_string); std::string utf8_string; if (target_length <= 0 || target_length > utf8_string.max_size()) { diff --git a/fastpair/rust/demo/windows/runner/win32_window.cpp b/fastpair/rust/demo/windows/runner/win32_window.cpp index 5fb3b91d..b3c2fa35 100644 --- a/fastpair/rust/demo/windows/runner/win32_window.cpp +++ b/fastpair/rust/demo/windows/runner/win32_window.cpp @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "win32_window.h" +#include "./win32_window.h" #include #include -#include "resource.h" +#include "./resource.h" namespace { @@ -38,7 +38,8 @@ constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; /// value indicates apps should use light mode. constexpr const wchar_t kGetPreferredBrightnessRegKey[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; -constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = + L"AppsUseLightTheme"; // The number of Win32Window objects that currently exist. static int g_active_window_count = 0; From 2c55c0cdbd6e7c7d6449d159284b1bbdbee4be5a Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Tue, 15 Aug 2023 15:52:28 -0700 Subject: [PATCH 096/128] Refactor Pipe implementation * Remove Pipe classes * Rename BasePipe to Pipe * Hide Pipe class in anonymous namespace in pipe.cc * Add CreatePipe static method * Replace Payload(std::function) with Payload(std::unique_ptr) * Find&Replace Pipe usages PiperOrigin-RevId: 557276980 --- connections/c/payload_w.cc | 18 +- connections/c/payload_w.h | 9 +- connections/implementation/BUILD | 2 + .../base_endpoint_channel_test.cc | 138 +++++++------- .../implementation/base_pcp_handler_test.cc | 73 +++++--- .../implementation/encryption_runner_test.cc | 32 ++-- .../endpoint_channel_manager_test.cc | 73 ++++---- .../internal_payload_factory.cc | 46 +++-- .../internal_payload_factory_test.cc | 24 +-- .../mediums/webrtc/webrtc_socket_impl.cc | 21 ++- .../mediums/webrtc/webrtc_socket_impl.h | 4 +- .../offline_service_controller_test.cc | 43 +++-- .../implementation/payload_manager_test.cc | 87 ++++----- connections/payload.cc | 27 ++- connections/payload.h | 9 +- connections/payload_test.cc | 14 +- connections/swift/NearbyCoreAdapter/BUILD | 1 - .../Sources/CPPInputStream.h | 45 +++++ .../Sources/CPPInputStream.mm | 46 +++++ .../Sources/CPPInputStreamBinding.h | 50 ------ .../Sources/CPPInputStreamBinding.mm | 90 ---------- .../Sources/GNCInputStream.h | 6 +- .../Sources/GNCInputStream.mm | 15 +- .../Sources/GNCPayload+CppConversions.h | 10 ++ .../Sources/GNCPayload+CppConversions.mm | 14 +- .../NearbyCoreAdapter/Sources/GNCPayload.mm | 3 - internal/platform/BUILD | 3 +- internal/platform/base_pipe.cc | 103 ----------- internal/platform/base_pipe.h | 136 -------------- internal/platform/bluetooth_classic_test.cc | 12 +- internal/platform/implementation/g3/BUILD | 4 +- internal/platform/implementation/g3/ble.cc | 8 + internal/platform/implementation/g3/ble.h | 1 - internal/platform/implementation/g3/ble_v2.cc | 12 +- internal/platform/implementation/g3/ble_v2.h | 1 - .../implementation/g3/bluetooth_classic.cc | 8 +- .../implementation/g3/bluetooth_classic.h | 1 - internal/platform/implementation/g3/pipe.h | 42 ----- .../platform/implementation/g3/socket_base.h | 73 +++++--- .../platform/implementation/g3/wifi_direct.cc | 8 +- .../platform/implementation/g3/wifi_direct.h | 1 - .../implementation/g3/wifi_hotspot.cc | 8 +- .../platform/implementation/g3/wifi_hotspot.h | 1 - .../platform/implementation/g3/wifi_lan.cc | 5 +- .../platform/implementation/g3/wifi_lan.h | 1 - internal/platform/pipe.cc | 168 +++++++++++++++++- internal/platform/pipe.h | 24 +-- internal/platform/pipe_test.cc | 133 +++++++------- 48 files changed, 795 insertions(+), 858 deletions(-) create mode 100644 connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.h create mode 100644 connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.mm delete mode 100644 connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.h delete mode 100644 connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.mm delete mode 100644 internal/platform/base_pipe.cc delete mode 100644 internal/platform/base_pipe.h delete mode 100644 internal/platform/implementation/g3/pipe.h diff --git a/connections/c/payload_w.cc b/connections/c/payload_w.cc index 6205ffda..5faf69c0 100644 --- a/connections/c/payload_w.cc +++ b/connections/c/payload_w.cc @@ -13,9 +13,16 @@ // limitations under the License. #include "connections/c/payload_w.h" +#include +#include +#include +#include + #include "connections/c/file_w.h" #include "connections/payload.h" +#include "connections/payload_type.h" #include "internal/platform/byte_array.h" +#include "internal/platform/input_stream.h" #include "internal/platform/payload_id.h" namespace nearby { @@ -32,7 +39,7 @@ PayloadW::PayloadW() : impl_(std::unique_ptr( new connections::Payload())) {} -PayloadW::~PayloadW() {} +PayloadW::~PayloadW() = default; PayloadW::PayloadW(PayloadW &&other) noexcept : impl_(std::move(other.impl_)) {} PayloadW &PayloadW::operator=(PayloadW &&other) noexcept { @@ -49,10 +56,9 @@ PayloadW::PayloadW(InputFileW &file) : impl_(std::unique_ptr( new connections::Payload(InputFile(std::move(*file.GetImpl()))))) {} -// TODO(jfcarroll): Convert std::function to function pointer -PayloadW::PayloadW(std::function stream) +PayloadW::PayloadW(std::unique_ptr stream) : impl_(std::unique_ptr( - new connections::Payload(stream))) {} + new connections::Payload(std::move(stream)))) {} // Constructors for incoming payloads. PayloadW::PayloadW(PayloadId id, const char *bytes, const size_t bytes_size) @@ -69,9 +75,9 @@ PayloadW::PayloadW(const char *parent_folder, const char *file_name, new connections::Payload(parent_folder, file_name, std::move(*file.GetImpl())))) {} -PayloadW::PayloadW(PayloadId id, std::function stream) +PayloadW::PayloadW(PayloadId id, std::unique_ptr stream) : impl_(std::unique_ptr( - new connections::Payload(id, stream))) {} + new connections::Payload(id, std::move(stream)))) {} // Returns ByteArray payload, if it has been defined, or empty ByteArray. bool PayloadW::AsBytes(const char *&bytes, size_t &bytes_size) const & { diff --git a/connections/c/payload_w.h b/connections/c/payload_w.h index d4baeaf1..17972e6e 100644 --- a/connections/c/payload_w.h +++ b/connections/c/payload_w.h @@ -60,19 +60,18 @@ class DLL_API PayloadW { ~PayloadW(); // Constructors for outgoing payloads. - explicit PayloadW(const char* bytes, const size_t size); + explicit PayloadW(const char* bytes, size_t size); explicit PayloadW(InputFileW& file); - explicit PayloadW(std::function stream); + explicit PayloadW(std::unique_ptr stream); // Constructors for incoming payloads. - PayloadW(PayloadId id, const char* bytes, const size_t size); + PayloadW(PayloadId id, const char* bytes, size_t size); PayloadW(PayloadId id, InputFileW file); explicit PayloadW(const char* parent_folder, const char* file_name, InputFileW file); - // TODO(jfcarroll): Convert std::function to function pointer - PayloadW(PayloadId id, std::function stream); + PayloadW(PayloadId id, std::unique_ptr stream); // Returns ByteArray payload, if it has // been defined, or empty ByteArray. bool AsBytes(const char*& bytes, size_t& bytes_size) const&; diff --git a/connections/implementation/BUILD b/connections/implementation/BUILD index 2179906b..fe357f98 100644 --- a/connections/implementation/BUILD +++ b/connections/implementation/BUILD @@ -226,6 +226,7 @@ cc_test( ":internal", ":internal_test", "//connections:core_types", + "//connections/implementation/analytics", "//connections/implementation/flags:connections_flags", "//connections/implementation/mediums", "//connections/implementation/proto:offline_wire_formats_cc_proto", @@ -241,6 +242,7 @@ cc_test( "//internal/test", "//proto:connections_enums_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/container:flat_hash_set", "@com_google_absl//absl/strings", "@com_google_absl//absl/strings:str_format", diff --git a/connections/implementation/base_endpoint_channel_test.cc b/connections/implementation/base_endpoint_channel_test.cc index 03ca6898..00646455 100644 --- a/connections/implementation/base_endpoint_channel_test.cc +++ b/connections/implementation/base_endpoint_channel_test.cc @@ -14,7 +14,9 @@ #include "connections/implementation/base_endpoint_channel.h" +#include #include +#include #include #include @@ -22,9 +24,13 @@ #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "absl/time/clock.h" #include "absl/time/time.h" +#include "connections/implementation/client_proxy.h" #include "connections/implementation/encryption_runner.h" +#include "connections/implementation/endpoint_channel.h" #include "connections/implementation/offline_frames.h" #include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" @@ -34,7 +40,6 @@ #include "internal/platform/multi_thread_executor.h" #include "internal/platform/output_stream.h" #include "internal/platform/pipe.h" -#include "internal/platform/single_thread_executor.h" #include "proto/connections_enums.pb.h" namespace nearby { @@ -44,6 +49,7 @@ namespace { using ::location::nearby::proto::connections::DisconnectionReason; using ::location::nearby::proto::connections::Medium; using EncryptionContext = BaseEndpointChannel::EncryptionContext; +constexpr size_t kChunkSize = 64 * 1024; class TestEndpointChannel : public BaseEndpointChannel { public: @@ -62,7 +68,7 @@ std::function MakeDataPump( return [label, input, output, monitor]() { NEARBY_LOGS(INFO) << "streaming data through '" << label << "'"; while (true) { - auto read_response = input->Read(Pipe::kChunkSize); + auto read_response = input->Read(kChunkSize); if (!read_response.ok()) { NEARBY_LOGS(INFO) << "Peer reader closed on '" << label << "'"; output->Close(); @@ -158,21 +164,17 @@ DoDhKeyExchange(BaseEndpointChannel* channel_a, } TEST(BaseEndpointChannelTest, ConstructorDestructorWorks) { - Pipe pipe; - InputStream& input_stream = pipe.GetInputStream(); - OutputStream& output_stream = pipe.GetOutputStream(); + auto [input, output] = CreatePipe(); - TestEndpointChannel test_channel(&input_stream, &output_stream); + TestEndpointChannel test_channel(input.get(), output.get()); } TEST(BaseEndpointChannelTest, ReadWrite) { // Direct not-encrypted IO. - Pipe pipe_a; // channel_a writes to pipe_a, reads from pipe_b. - Pipe pipe_b; // channel_b writes to pipe_b, reads from pipe_a. - TestEndpointChannel channel_a(&pipe_b.GetInputStream(), - &pipe_a.GetOutputStream()); - TestEndpointChannel channel_b(&pipe_a.GetInputStream(), - &pipe_b.GetOutputStream()); + 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()); ByteArray tx_message{"data message"}; channel_a.Write(tx_message); ByteArray rx_message = std::move(channel_b.Read().result()); @@ -180,8 +182,8 @@ TEST(BaseEndpointChannelTest, ReadWrite) { } TEST(BaseEndpointChannelTest, ChannelUnencryptedByDefault) { - Pipe pipe; - TestEndpointChannel channel(&pipe.GetInputStream(), &pipe.GetOutputStream()); + auto pipe = CreatePipe(); + TestEndpointChannel channel(pipe.first.get(), pipe.second.get()); ExceptionOr result = channel.TryDecrypt(ByteArray("message")); @@ -192,12 +194,10 @@ TEST(BaseEndpointChannelTest, ChannelUnencryptedByDefault) { TEST(BaseEndpointChannelTest, TryDecrypt) { absl::string_view kMessage = "message"; - Pipe pipe_a; // channel_a writes to pipe_a, reads from pipe_b. - Pipe pipe_b; // channel_b writes to pipe_b, reads from pipe_a. - TestEndpointChannel channel_a(&pipe_b.GetInputStream(), - &pipe_a.GetOutputStream()); - TestEndpointChannel channel_b(&pipe_a.GetInputStream(), - &pipe_b.GetOutputStream()); + 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); ASSERT_NE(context_a, nullptr); ASSERT_NE(context_b, nullptr); @@ -215,12 +215,10 @@ TEST(BaseEndpointChannelTest, TryDecrypt) { } TEST(BaseEndpointChannelTest, TryDecryptFailsWhenDecryptionFails) { - Pipe pipe_a; // channel_a writes to pipe_a, reads from pipe_b. - Pipe pipe_b; // channel_b writes to pipe_b, reads from pipe_a. - TestEndpointChannel channel_a(&pipe_b.GetInputStream(), - &pipe_a.GetOutputStream()); - TestEndpointChannel channel_b(&pipe_a.GetInputStream(), - &pipe_b.GetOutputStream()); + 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); ASSERT_NE(context_a, nullptr); channel_a.EnableEncryption(context_a); @@ -240,25 +238,27 @@ TEST(BaseEndpointChannelTest, NotEncryptedReadWriteCanBeIntercepted) { absl::Mutex mutex; std::string capture_a; std::string capture_b; - Pipe client_a; // Channel "a" writes to client "a", reads from server "a". - Pipe client_b; // Channel "b" writes to client "b", reads from server "b". - Pipe server_a; // Data pump "a" reads from client "a", writes to server "b". - Pipe server_b; // Data pump "b" reads from client "b", writes to server "a". - TestEndpointChannel channel_a(&server_a.GetInputStream(), - &client_a.GetOutputStream()); - TestEndpointChannel channel_b(&server_b.GetInputStream(), - &client_b.GetOutputStream()); + auto client_a = + CreatePipe(); // Channel "a" writes to client "a", reads from server "a". + auto client_b = + CreatePipe(); // Channel "b" writes to client "b", reads from server "b". + auto server_a = CreatePipe(); // Data pump "a" reads from client "a", writes + // 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()); ON_CALL(channel_a, GetMedium).WillByDefault([]() { return Medium::BLE; }); ON_CALL(channel_b, GetMedium).WillByDefault([]() { return Medium::BLE; }); MultiThreadExecutor executor(2); - executor.Execute(MakeDataPump( - "pump_a", &client_a.GetInputStream(), &server_b.GetOutputStream(), - MakeDataMonitor("monitor_a", &capture_a, &mutex))); - executor.Execute(MakeDataPump( - "pump_b", &client_b.GetInputStream(), &server_a.GetOutputStream(), - MakeDataMonitor("monitor_b", &capture_b, &mutex))); + executor.Execute( + MakeDataPump("pump_a", client_a.first.get(), server_b.second.get(), + MakeDataMonitor("monitor_a", &capture_a, &mutex))); + executor.Execute( + MakeDataPump("pump_b", client_b.first.get(), server_a.second.get(), + MakeDataMonitor("monitor_b", &capture_b, &mutex))); EXPECT_EQ(channel_a.GetType(), "BLE"); EXPECT_EQ(channel_b.GetType(), "BLE"); @@ -289,14 +289,16 @@ TEST(BaseEndpointChannelTest, EncryptedReadWriteCanNotBeIntercepted) { absl::Mutex mutex; std::string capture_a; std::string capture_b; - Pipe client_a; // Channel "a" writes to client "a", reads from server "a". - Pipe client_b; // Channel "b" writes to client "b", reads from server "b". - Pipe server_a; // Data pump "a" reads from client "a", writes to server "b". - Pipe server_b; // Data pump "b" reads from client "b", writes to server "a". - TestEndpointChannel channel_a(&server_a.GetInputStream(), - &client_a.GetOutputStream()); - TestEndpointChannel channel_b(&server_b.GetInputStream(), - &client_b.GetOutputStream()); + auto client_a = + CreatePipe(); // Channel "a" writes to client "a", reads from server "a". + auto client_b = + CreatePipe(); // Channel "b" writes to client "b", reads from server "b". + auto server_a = CreatePipe(); // Data pump "a" reads from client "a", writes + // 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()); ON_CALL(channel_a, GetMedium).WillByDefault([]() { return Medium::BLUETOOTH; @@ -306,12 +308,12 @@ TEST(BaseEndpointChannelTest, EncryptedReadWriteCanNotBeIntercepted) { }); MultiThreadExecutor executor(2); - executor.Execute(MakeDataPump( - "pump_a", &client_a.GetInputStream(), &server_b.GetOutputStream(), - MakeDataMonitor("monitor_a", &capture_a, &mutex))); - executor.Execute(MakeDataPump( - "pump_b", &client_b.GetInputStream(), &server_a.GetOutputStream(), - MakeDataMonitor("monitor_b", &capture_b, &mutex))); + executor.Execute( + MakeDataPump("pump_a", client_a.first.get(), server_b.second.get(), + MakeDataMonitor("monitor_a", &capture_a, &mutex))); + executor.Execute( + MakeDataPump("pump_b", client_b.first.get(), server_a.second.get(), + 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); @@ -346,12 +348,10 @@ TEST(BaseEndpointChannelTest, EncryptedReadWriteCanNotBeIntercepted) { TEST(BaseEndpointChannelTest, CanBesuspendedAndResumed) { // Setup test communication environment. - Pipe pipe_a; // channel_a writes to pipe_a, reads from pipe_b. - Pipe pipe_b; // channel_b writes to pipe_b, reads from pipe_a. - TestEndpointChannel channel_a(&pipe_b.GetInputStream(), - &pipe_a.GetOutputStream()); - TestEndpointChannel channel_b(&pipe_a.GetInputStream(), - &pipe_b.GetOutputStream()); + 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()); ON_CALL(channel_a, GetMedium).WillByDefault([]() { return Medium::WIFI_LAN; @@ -399,14 +399,12 @@ TEST(BaseEndpointChannelTest, CanBesuspendedAndResumed) { } TEST(BaseEndpointChannelTest, ReadAfterInputStreamClosed) { - Pipe pipe; - InputStream& input_stream = pipe.GetInputStream(); - OutputStream& output_stream = pipe.GetOutputStream(); + auto [input, output] = CreatePipe(); - TestEndpointChannel test_channel(&input_stream, &output_stream); + TestEndpointChannel test_channel(input.get(), output.get()); // Close the output stream before trying to read from the input. - output_stream.Close(); + output->Close(); // Trying to read should fail gracefully with an IO error. ExceptionOr read_data = test_channel.Read(); @@ -417,12 +415,10 @@ TEST(BaseEndpointChannelTest, ReadAfterInputStreamClosed) { TEST(BaseEndpointChannelTest, ReadUnencryptedFrameOnEncryptedChannel) { // Setup test communication environment. - Pipe pipe_a; // channel_a writes to pipe_a, reads from pipe_b. - Pipe pipe_b; // channel_b writes to pipe_b, reads from pipe_a. - TestEndpointChannel channel_a(&pipe_b.GetInputStream(), - &pipe_a.GetOutputStream()); - TestEndpointChannel channel_b(&pipe_a.GetInputStream(), - &pipe_b.GetOutputStream()); + 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()); ON_CALL(channel_a, GetMedium).WillByDefault([]() { return Medium::BLUETOOTH; diff --git a/connections/implementation/base_pcp_handler_test.cc b/connections/implementation/base_pcp_handler_test.cc index 9e849bef..ddb38e7e 100644 --- a/connections/implementation/base_pcp_handler_test.cc +++ b/connections/implementation/base_pcp_handler_test.cc @@ -18,21 +18,32 @@ #include #include #include +#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" +#include "absl/time/clock.h" #include "absl/time/time.h" +#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/base_endpoint_channel.h" #include "connections/implementation/bwu_manager.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/encryption_runner.h" +#include "connections/implementation/endpoint_manager.h" +#include "connections/implementation/mediums/mediums.h" #include "connections/implementation/offline_frames.h" +#include "connections/implementation/pcp.h" #include "connections/implementation/proto/offline_wire_formats.pb.h" #include "connections/listeners.h" #include "connections/medium_selector.h" +#include "connections/out_of_band_connection_metadata.h" #include "connections/params.h" #include "connections/status.h" #include "connections/strategy.h" @@ -41,7 +52,12 @@ #include "internal/interop/device_provider.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/input_stream.h" +#include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" +#include "internal/platform/output_stream.h" #include "internal/platform/pipe.h" #include "proto/connections_enums.pb.h" #include "proto/connections_enums.proto.h" @@ -108,9 +124,12 @@ class FakePresenceDeviceProvider : public NearbyDeviceProvider { class MockEndpointChannel : public BaseEndpointChannel { public: - explicit MockEndpointChannel(Pipe* reader, Pipe* writer) - : BaseEndpointChannel("service_id", "channel", &reader->GetInputStream(), - &writer->GetOutputStream()) {} + explicit MockEndpointChannel(std::unique_ptr reader, + std::unique_ptr writer) + : BaseEndpointChannel("service_id", "channel", reader.get(), + writer.get()), + input_stream_(std::move(reader)), + output_stream_(std::move(writer)) {} ExceptionOr DoRead() { return BaseEndpointChannel::Read(); } Exception DoWrite(const ByteArray& data) { @@ -136,6 +155,10 @@ class MockEndpointChannel : public BaseEndpointChannel { MOCK_METHOD(absl::Time, GetLastReadTimestamp, (), (const override)); bool broken_write_{false}; + + private: + std::unique_ptr input_stream_; + std::unique_ptr output_stream_; }; class MockPcpHandler : public BasePcpHandler { @@ -459,10 +482,13 @@ class BasePcpHandlerTest std::pair, std::unique_ptr> SetupConnection( - Pipe& pipe_a, Pipe& pipe_b, location::nearby::proto::connections::Medium medium) { // NOLINT - auto channel_a = std::make_unique(&pipe_b, &pipe_a); - auto channel_b = std::make_unique(&pipe_a, &pipe_b); + auto [input_a, output_a] = CreatePipe(); + auto [input_b, output_b] = CreatePipe(); + auto channel_a = std::make_unique(std::move(input_a), + std::move(output_b)); + auto channel_b = std::make_unique(std::move(input_b), + std::move(output_a)); // On initiator (A) side, we drop the first write, since this is a // connection establishment packet, and we don't have the peer entity, just // the peer channel. The rest of the exchange must happen for the benefit of @@ -648,9 +674,6 @@ class BasePcpHandlerTest expected_result); NEARBY_LOG(INFO, "Stopping Encryption Runner"); } - - Pipe pipe_a_; - Pipe pipe_b_; MockConnectionListener mock_connection_listener_; MockDiscoveryListener mock_discovery_listener_; ConnectionListener connection_listener_{ @@ -839,7 +862,7 @@ TEST_F(BasePcpHandlerTest, WifiMediumFailFallBackToBT) { auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); @@ -865,7 +888,7 @@ TEST_P(BasePcpHandlerTest, RequestConnectionChangesState) { StartDiscovery(&client, &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); @@ -910,7 +933,7 @@ TEST_P(BasePcpHandlerTest, CanRequestConnectionPresence) { StartDiscovery(&client, &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); @@ -941,7 +964,7 @@ TEST_P(BasePcpHandlerTest, CanRequestConnectionLegacy) { StartDiscovery(&client, &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); @@ -968,7 +991,7 @@ TEST_P(BasePcpHandlerTest, IoError_RequestConnectionFails) { StartDiscovery(&client, &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(AtLeast(1)); @@ -997,7 +1020,7 @@ TEST_P(BasePcpHandlerTest, AcceptConnectionChangesState) { StartDiscovery(&client, &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); @@ -1028,7 +1051,7 @@ TEST_P(BasePcpHandlerTest, RejectConnectionChangesState) { StartDiscovery(&client, &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_b = channel_pair.second; EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(1); RequestConnection(endpoint_id, std::move(channel_pair.first), channel_b.get(), @@ -1056,7 +1079,7 @@ TEST_P(BasePcpHandlerTest, OnIncomingFrameChangesState) { StartDiscovery(&client, &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); @@ -1098,7 +1121,7 @@ TEST_P(BasePcpHandlerTest, DestructorIsCalledOnProtocolEndpoint) { StartDiscovery(&client, &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); @@ -1141,7 +1164,7 @@ TEST_P(BasePcpHandlerTest, MultipleMediumsProduceSingleEndpointLostEvent) { StartDiscovery(&client, &pcp_handler); auto mediums = pcp_handler.GetDiscoveryMediums(&client); auto connect_medium = mediums[mediums.size() - 1]; - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto channel_pair = SetupConnection(connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); @@ -1582,7 +1605,7 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForConnectionsWithUnknown) { .first.Ok()); ASSERT_TRUE(client.IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(&client)); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, Medium::BLUETOOTH); + auto channel_pair = SetupConnection(Medium::BLUETOOTH); ByteArray serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", @@ -1631,7 +1654,7 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForPresenceWithUnknown) { .first.Ok()); ASSERT_TRUE(client.IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(&client)); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, Medium::BLUETOOTH); + auto channel_pair = SetupConnection(Medium::BLUETOOTH); ByteArray serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", @@ -1681,7 +1704,7 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForPresenceWithConnections) { .first.Ok()); ASSERT_TRUE(client.IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(&client)); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, Medium::BLUETOOTH); + auto channel_pair = SetupConnection(Medium::BLUETOOTH); ByteArray serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", @@ -1732,7 +1755,7 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForPresenceWithPresence) { .first.Ok()); ASSERT_TRUE(client.IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(&client)); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, Medium::BLUETOOTH); + auto channel_pair = SetupConnection(Medium::BLUETOOTH); ByteArray serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", @@ -1782,7 +1805,7 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForConnectionsWithConnections) { .first.Ok()); ASSERT_TRUE(client.IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(&client)); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, Medium::BLUETOOTH); + auto channel_pair = SetupConnection(Medium::BLUETOOTH); ByteArray serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", @@ -1832,7 +1855,7 @@ TEST_F(BasePcpHandlerTest, TestDeviceFilterForConnectionsWithPresence) { .first.Ok()); ASSERT_TRUE(client.IsListeningForIncomingConnections()); ASSERT_TRUE(pcp_handler.CanReceiveIncomingConnection(&client)); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_, Medium::BLUETOOTH); + auto channel_pair = SetupConnection(Medium::BLUETOOTH); ByteArray serialized_frame = parser::ForConnectionRequestConnections( {}, { .local_endpoint_id = "ABCD", diff --git a/connections/implementation/encryption_runner_test.cc b/connections/implementation/encryption_runner_test.cc index 0bc91960..4dbd624e 100644 --- a/connections/implementation/encryption_runner_test.cc +++ b/connections/implementation/encryption_runner_test.cc @@ -14,23 +14,30 @@ #include "connections/implementation/encryption_runner.h" -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" +#include +#include + #include "gtest/gtest.h" -#include "absl/time/clock.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/byte_array.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/exception.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/output_stream.h" #include "internal/platform/pipe.h" #include "internal/platform/system_clock.h" #include "proto/connections_enums.pb.h" +#include "third_party/ukey2/src/main/cpp/include/securegcm/ukey2_handshake.h" namespace nearby { namespace connections { namespace { using ::location::nearby::proto::connections::Medium; +constexpr size_t kChunkSize = 64 * 1024; class FakeEndpointChannel : public EndpointChannel { public: @@ -38,13 +45,11 @@ class FakeEndpointChannel : public EndpointChannel { : in_(in), out_(out) {} ExceptionOr Read() override { read_timestamp_ = SystemClock::ElapsedRealtime(); - return in_ ? in_->Read(Pipe::kChunkSize) - : ExceptionOr{Exception::kIo}; + return in_ ? in_->Read(kChunkSize) : ExceptionOr{Exception::kIo}; } ExceptionOr Read(PacketMetaData& packet_meta_data) override { read_timestamp_ = SystemClock::ElapsedRealtime(); - return in_ ? in_->Read(Pipe::kChunkSize) - : ExceptionOr{Exception::kIo}; + return in_ ? in_->Read(kChunkSize) : ExceptionOr{Exception::kIo}; } Exception Write(const ByteArray& data) override { write_timestamp_ = SystemClock::ElapsedRealtime(); @@ -103,8 +108,7 @@ class FakeEndpointChannel : public EndpointChannel { }; struct User { - User(Pipe* reader, Pipe* writer) - : channel(&reader->GetInputStream(), &writer->GetOutputStream()) {} + User(InputStream* reader, OutputStream* writer) : channel(reader, writer) {} FakeEndpointChannel channel; EncryptionRunner crypto; @@ -126,10 +130,12 @@ struct Response { TEST(EncryptionRunnerTest, ConstructorDestructorWorks) { EncryptionRunner enc; } TEST(EncryptionRunnerTest, ReadWrite) { - Pipe from_a_to_b; - Pipe from_b_to_a; - User user_a(/*reader=*/&from_b_to_a, /*writer=*/&from_a_to_b); - User user_b(/*reader=*/&from_a_to_b, /*writer=*/&from_b_to_a); + auto from_a_to_b = CreatePipe(); + auto from_b_to_a = CreatePipe(); + User user_a(/*reader=*/from_b_to_a.first.get(), + /*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; user_a.crypto.StartServer( diff --git a/connections/implementation/endpoint_channel_manager_test.cc b/connections/implementation/endpoint_channel_manager_test.cc index fe34336b..466f67c0 100644 --- a/connections/implementation/endpoint_channel_manager_test.cc +++ b/connections/implementation/endpoint_channel_manager_test.cc @@ -14,20 +14,24 @@ #include "connections/implementation/endpoint_channel_manager.h" +#include #include #include #include #include -#include "securegcm/d2d_connection_context_v1.h" #include "securegcm/ukey2_handshake.h" #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" #include "connections/implementation/base_endpoint_channel.h" +#include "connections/implementation/client_proxy.h" #include "connections/implementation/encryption_runner.h" +#include "connections/implementation/endpoint_channel.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" @@ -45,6 +49,7 @@ using ::location::nearby::proto::connections::DisconnectionReason; using ::location::nearby::proto::connections::Medium; using EncryptionContext = BaseEndpointChannel::EncryptionContext; +constexpr size_t kChunkSize = 64 * 1024; constexpr absl::string_view kEndpointId = "EndpointId"; constexpr absl::string_view kMonitorA = "MonitorA"; constexpr absl::string_view kMonitorB = "MonitorB"; @@ -66,7 +71,7 @@ std::function MakeDataPump( return [label, input, output, monitor]() { NEARBY_LOGS(INFO) << "streaming data through '" << label << "'"; while (true) { - auto read_response = input->Read(Pipe::kChunkSize); + auto read_response = input->Read(kChunkSize); if (!read_response.ok()) { NEARBY_LOGS(INFO) << "Peer reader closed on '" << label << "'"; output->Close(); @@ -169,14 +174,18 @@ TEST(BaseEndpointChannelManagerTest, RegisterChannelEncryptedReadwrite) { std::string capture_b; ClientProxy proxy_a; ClientProxy proxy_b; - Pipe client_a; // Channel "a" writes to client "a", reads from server "a". - Pipe client_b; // Channel "b" writes to client "b", reads from server "b". - Pipe server_a; // Data pump "a" reads from client "a", writes to server "b". - Pipe server_b; // Data pump "b" reads from client "b", writes to server "a". - auto channel_a = std::make_unique( - &server_a.GetInputStream(), &client_a.GetOutputStream()); - auto channel_b = std::make_unique( - &server_b.GetInputStream(), &client_b.GetOutputStream()); + auto client_a = + CreatePipe(); // Channel "a" writes to client "a", reads from server "a". + auto client_b = + CreatePipe(); // Channel "b" writes to client "b", reads from server "b". + auto server_a = CreatePipe(); // Data pump "a" reads from client "a", writes + // 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(), + client_a.second.get()); + auto channel_b = std::make_unique(server_b.first.get(), + client_b.second.get()); auto channel_a_raw = channel_a.get(); auto channel_b_raw = channel_b.get(); @@ -188,12 +197,12 @@ TEST(BaseEndpointChannelManagerTest, RegisterChannelEncryptedReadwrite) { }); MultiThreadExecutor executor(2); - executor.Execute(MakeDataPump( - kPumpA, &client_a.GetInputStream(), &server_b.GetOutputStream(), - MakeDataMonitor(kMonitorA, &capture_a, &mutex))); - executor.Execute(MakeDataPump( - kPumpB, &client_b.GetInputStream(), &server_a.GetOutputStream(), - MakeDataMonitor(kMonitorB, &capture_b, &mutex))); + executor.Execute( + MakeDataPump(kPumpA, client_a.first.get(), server_b.second.get(), + MakeDataMonitor(kMonitorA, &capture_a, &mutex))); + executor.Execute( + MakeDataPump(kPumpB, client_b.first.get(), server_a.second.get(), + MakeDataMonitor(kMonitorB, &capture_b, &mutex))); // Run DH key exchange; setup encryption contexts for channels. auto context = DoDhKeyExchange(channel_a.get(), channel_b.get()); @@ -240,14 +249,18 @@ TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) { std::string capture_b; ClientProxy proxy_a; ClientProxy proxy_b; - Pipe client_a; // Channel "a" writes to client "a", reads from server "a". - Pipe client_b; // Channel "b" writes to client "b", reads from server "b". - Pipe server_a; // Data pump "a" reads from client "a", writes to server "b". - Pipe server_b; // Data pump "b" reads from client "b", writes to server "a". - auto channel_a = std::make_unique( - &server_a.GetInputStream(), &client_a.GetOutputStream()); - auto channel_b = std::make_unique( - &server_b.GetInputStream(), &client_b.GetOutputStream()); + auto client_a = + CreatePipe(); // Channel "a" writes to client "a", reads from server "a". + auto client_b = + CreatePipe(); // Channel "b" writes to client "b", reads from server "b". + auto server_a = CreatePipe(); // Data pump "a" reads from client "a", writes + // 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(), + client_a.second.get()); + auto channel_b = std::make_unique(server_b.first.get(), + client_b.second.get()); auto channel_a_raw = channel_a.get(); auto channel_b_raw = channel_b.get(); @@ -259,12 +272,12 @@ TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) { }); MultiThreadExecutor executor(2); - executor.Execute(MakeDataPump( - kPumpA, &client_a.GetInputStream(), &server_b.GetOutputStream(), - MakeDataMonitor(kMonitorA, &capture_a, &mutex))); - executor.Execute(MakeDataPump( - kPumpB, &client_b.GetInputStream(), &server_a.GetOutputStream(), - MakeDataMonitor(kMonitorB, &capture_b, &mutex))); + executor.Execute( + MakeDataPump(kPumpA, client_a.first.get(), server_b.second.get(), + MakeDataMonitor(kMonitorA, &capture_a, &mutex))); + executor.Execute( + MakeDataPump(kPumpB, client_b.first.get(), server_a.second.get(), + MakeDataMonitor(kMonitorB, &capture_b, &mutex))); // Run DH key exchange; setup encryption contexts for channels. auto context = DoDhKeyExchange(channel_a.get(), channel_b.get()); diff --git a/connections/implementation/internal_payload_factory.cc b/connections/implementation/internal_payload_factory.cc index 59d7de9d..38268fc9 100644 --- a/connections/implementation/internal_payload_factory.cc +++ b/connections/implementation/internal_payload_factory.cc @@ -14,24 +14,25 @@ #include "connections/implementation/internal_payload_factory.h" +#include #include #include #include #include -#include "absl/memory/memory.h" -#include "connections/implementation/offline_frames_validator.h" +#include "absl/strings/str_cat.h" +#include "connections/implementation/internal_payload.h" +#include "connections/implementation/proto/offline_wire_formats.pb.h" #include "connections/payload.h" +#include "connections/payload_type.h" #include "internal/platform/byte_array.h" -#include "internal/platform/condition_variable.h" #include "internal/platform/exception.h" -#include "internal/platform/feature_flags.h" #include "internal/platform/file.h" #include "internal/platform/implementation/platform.h" -#include "internal/platform/implementation/shared/file.h" +#include "internal/platform/input_stream.h" #include "internal/platform/logging.h" -#include "internal/platform/mutex.h" #include "internal/platform/os_name.h" +#include "internal/platform/output_stream.h" #include "internal/platform/pipe.h" namespace nearby { @@ -155,8 +156,9 @@ class OutgoingStreamInternalPayload : public InternalPayload { class IncomingStreamInternalPayload : public InternalPayload { public: - IncomingStreamInternalPayload(Payload payload, std::shared_ptr pipe) - : InternalPayload(std::move(payload)), pipe_(pipe) {} + IncomingStreamInternalPayload(Payload payload, + std::unique_ptr output) + : InternalPayload(std::move(payload)), output_(std::move(output)) {} PayloadTransferFrame::PayloadHeader::PayloadType GetType() const override { return PayloadTransferFrame::PayloadHeader::STREAM; @@ -174,7 +176,7 @@ class IncomingStreamInternalPayload : public InternalPayload { return {Exception::kSuccess}; } - return pipe_->GetOutputStream().Write(chunk); + return output_->Write(chunk); } ExceptionOr SkipToOffset(size_t offset) override { @@ -183,10 +185,10 @@ class IncomingStreamInternalPayload : public InternalPayload { return {Exception::kIo}; } - void Close() override { pipe_->GetOutputStream().Close(); } + void Close() override { output_->Close(); } private: - std::shared_ptr pipe_; + std::unique_ptr output_; }; class OutgoingFileInternalPayload : public InternalPayload { @@ -311,14 +313,14 @@ std::unique_ptr CreateOutgoingInternalPayload( Payload payload) { switch (payload.GetType()) { case PayloadType::kBytes: - return absl::make_unique(std::move(payload)); + return std::make_unique(std::move(payload)); case PayloadType::kFile: { - return absl::make_unique(std::move(payload)); + return std::make_unique(std::move(payload)); } case PayloadType::kStream: - return absl::make_unique( + return std::make_unique( std::move(payload)); default: @@ -359,19 +361,15 @@ std::unique_ptr CreateIncomingInternalPayload( const Payload::Id payload_id = frame.payload_header().id(); switch (frame.payload_header().type()) { case PayloadTransferFrame::PayloadHeader::BYTES: { - return absl::make_unique( + return std::make_unique( Payload(payload_id, ByteArray(frame.payload_chunk().body()))); } case PayloadTransferFrame::PayloadHeader::STREAM: { - auto pipe = std::make_shared(); + auto [input, output] = CreatePipe(); - return absl::make_unique( - Payload(payload_id, - [pipe]() -> InputStream& { - return pipe->GetInputStream(); // NOLINT - }), - pipe); + return std::make_unique( + Payload(payload_id, std::move(input)), std::move(output)); } case PayloadTransferFrame::PayloadHeader::FILE: { @@ -411,11 +409,11 @@ std::unique_ptr CreateIncomingInternalPayload( // there will be no input file to open. // On Chrome the file path should be empty, so use the payload id. if (ImplementationPlatform::GetCurrentOS() == OSName::kChromeOS) { - return absl::make_unique( + return std::make_unique( Payload(payload_id, InputFile(payload_id, total_size)), OutputFile(payload_id), total_size); } else { - return absl::make_unique( + return std::make_unique( Payload(payload_id, parent_folder, file_name, InputFile(file_path, total_size)), OutputFile(file_path), total_size); diff --git a/connections/implementation/internal_payload_factory_test.cc b/connections/implementation/internal_payload_factory_test.cc index d17556e1..9f6979ae 100644 --- a/connections/implementation/internal_payload_factory_test.cc +++ b/connections/implementation/internal_payload_factory_test.cc @@ -14,15 +14,19 @@ #include "connections/implementation/internal_payload_factory.h" +#include +#include #include #include -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" -#include "connections/implementation/offline_frames.h" +#include "connections/implementation/internal_payload.h" #include "connections/implementation/proto/offline_wire_formats.pb.h" +#include "connections/payload.h" +#include "connections/payload_type.h" #include "internal/platform/byte_array.h" +#include "internal/platform/exception.h" +#include "internal/platform/file.h" #include "internal/platform/pipe.h" namespace nearby { @@ -44,11 +48,9 @@ TEST(InternalPayloadFactoryTest, CanCreateInternalPayloadFromBytePayload) { } TEST(InternalPayloadFactoryTest, CanCreateInternalPayloadFromStreamPayload) { - auto pipe = std::make_shared(); + auto [input, output] = CreatePipe(); std::unique_ptr internal_payload = - CreateOutgoingInternalPayload(Payload{[pipe]() -> InputStream& { - return pipe->GetInputStream(); // NOLINT - }}); + CreateOutgoingInternalPayload(Payload(std::move(input))); EXPECT_NE(internal_payload, nullptr); Payload payload = internal_payload->ReleasePayload(); EXPECT_EQ(payload.AsFile(), nullptr); @@ -212,13 +214,11 @@ TEST(InternalPayloadFactoryTest, SkipToOffset_StreamPayloadValidOffset_SkipsOffset) { ByteArray contents("0123456789"); constexpr size_t kOffset = 6; - auto pipe = std::make_shared(); + auto [input, output] = CreatePipe(); std::unique_ptr internal_payload = - CreateOutgoingInternalPayload(Payload{[pipe]() -> InputStream& { - return pipe->GetInputStream(); // NOLINT - }}); + CreateOutgoingInternalPayload(Payload(std::move(input))); EXPECT_NE(internal_payload, nullptr); - pipe->GetOutputStream().Write(contents); + output->Write(contents); ExceptionOr result = internal_payload->SkipToOffset(kOffset); diff --git a/connections/implementation/mediums/webrtc/webrtc_socket_impl.cc b/connections/implementation/mediums/webrtc/webrtc_socket_impl.cc index 63e66752..96d1b9bb 100644 --- a/connections/implementation/mediums/webrtc/webrtc_socket_impl.cc +++ b/connections/implementation/mediums/webrtc/webrtc_socket_impl.cc @@ -12,10 +12,18 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include +#include +#include +#include + +#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" @@ -61,6 +69,7 @@ WebRtcSocket::WebRtcSocket( : name_(name), data_channel_(std::move(data_channel)) { NEARBY_LOGS(INFO) << "WebRtcSocket::WebRtcSocket(" << name_ << ") this: " << this; + std::tie(pipe_input_, pipe_output_) = CreatePipe(); data_channel_->RegisterObserver(this); } @@ -77,7 +86,7 @@ WebRtcSocket::~WebRtcSocket() { << ") this: " << this << " done"; } -InputStream& WebRtcSocket::GetInputStream() { return pipe_.GetInputStream(); } +InputStream& WebRtcSocket::GetInputStream() { return *pipe_input_; } OutputStream& WebRtcSocket::GetOutputStream() { return output_stream_; } @@ -129,12 +138,12 @@ void WebRtcSocket::OnMessage(const webrtc::DataBuffer& buffer) { // we don't block signaling. OffloadFromSignalingThread( [this, buffer = ByteArray(buffer.data.data(), buffer.size())] { - if (!pipe_.GetOutputStream().Write(buffer).Ok()) { + if (!pipe_output_->Write(buffer).Ok()) { Close(); return; } - if (!pipe_.GetOutputStream().Flush().Ok()) { + if (!pipe_output_->Flush().Ok()) { Close(); } }); @@ -159,8 +168,8 @@ void WebRtcSocket::ClosePipe() { // 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_.GetInputStream().Close(); - pipe_.GetOutputStream().Close(); + pipe_input_->Close(); + pipe_output_->Close(); WakeUpWriter(); NEARBY_LOGS(INFO) << "WebRtcSocket::ClosePipe(" << name_ << ") this: " << this << " done"; diff --git a/connections/implementation/mediums/webrtc/webrtc_socket_impl.h b/connections/implementation/mediums/webrtc/webrtc_socket_impl.h index beb1000d..723280b8 100644 --- a/connections/implementation/mediums/webrtc/webrtc_socket_impl.h +++ b/connections/implementation/mediums/webrtc/webrtc_socket_impl.h @@ -100,8 +100,8 @@ class WebRtcSocket : public Socket, public webrtc::DataChannelObserver { std::string name_; rtc::scoped_refptr data_channel_; - Pipe pipe_; - + std::unique_ptr pipe_input_; + std::unique_ptr pipe_output_; OutputStreamImpl output_stream_{this}; AtomicBoolean closed_{false}; diff --git a/connections/implementation/offline_service_controller_test.cc b/connections/implementation/offline_service_controller_test.cc index 43daafea..5b4e0989 100644 --- a/connections/implementation/offline_service_controller_test.cc +++ b/connections/implementation/offline_service_controller_test.cc @@ -12,31 +12,42 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "connections/implementation/offline_service_controller.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 "absl/time/time.h" +#include "connections/advertising_options.h" +#include "connections/discovery_options.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/offline_simulation_user.h" +#include "connections/listeners.h" +#include "connections/medium_selector.h" +#include "connections/out_of_band_connection_metadata.h" +#include "connections/payload.h" #include "connections/status.h" +#include "connections/strategy.h" #include "internal/flags/nearby_flags.h" +#include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/input_stream.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" #include "internal/platform/output_stream.h" #include "internal/platform/pipe.h" -#include "internal/platform/system_clock.h" +#include "proto/connections_enums.proto.h" namespace nearby { namespace connections { namespace { using ::testing::Eq; - +constexpr size_t kChunkSize = 64 * 1024; constexpr std::array kFakeMacAddress = {'a', 'b', 'c', 'd', 'e', 'f'}; constexpr absl::string_view kServiceId = "service-id"; constexpr absl::string_view kDeviceA = "device-a"; @@ -302,12 +313,9 @@ TEST_P(OfflineServiceControllerTest, CanSendStreamPayload) { user_b.ExpectPayload(payload_latch_); ASSERT_TRUE(SetupConnection(user_a, user_b)); ByteArray message(std::string{kMessage}); - auto pipe = std::make_shared(); - OutputStream& tx = pipe->GetOutputStream(); - user_a.SendPayload(Payload([pipe]() -> InputStream& { - return pipe->GetInputStream(); // NOLINT - })); - tx.Write(message); + auto [input, tx] = CreatePipe(); + user_a.SendPayload(Payload(std::move(input))); + tx->Write(message); EXPECT_TRUE(payload_latch_.Await(kLongTimeout)); ASSERT_NE(user_b.GetPayload().AsStream(), nullptr); InputStream& rx = *user_b.GetPayload().AsStream(); @@ -316,7 +324,7 @@ TEST_P(OfflineServiceControllerTest, CanSendStreamPayload) { return info.bytes_transferred >= size; }, kLongTimeout)); - EXPECT_EQ(rx.Read(Pipe::kChunkSize).result(), message); + EXPECT_EQ(rx.Read(kChunkSize).result(), message); user_a.Stop(); user_b.Stop(); env_.Stop(); @@ -329,12 +337,9 @@ TEST_P(OfflineServiceControllerTest, CanCancelStreamPayload) { user_b.ExpectPayload(payload_latch_); ASSERT_TRUE(SetupConnection(user_a, user_b)); ByteArray message(std::string{kMessage}); - auto pipe = std::make_shared(); - OutputStream& tx = pipe->GetOutputStream(); - user_a.SendPayload(Payload([pipe]() -> InputStream& { - return pipe->GetInputStream(); // NOLINT - })); - tx.Write(message); + auto [input, tx] = CreatePipe(); + user_a.SendPayload(Payload(std::move(input))); + tx->Write(message); EXPECT_TRUE(payload_latch_.Await(kLongTimeout)); ASSERT_NE(user_b.GetPayload().AsStream(), nullptr); InputStream& rx = *user_b.GetPayload().AsStream(); @@ -343,11 +348,11 @@ TEST_P(OfflineServiceControllerTest, CanCancelStreamPayload) { return info.bytes_transferred >= size; }, kLongTimeout)); - EXPECT_EQ(rx.Read(Pipe::kChunkSize).result(), message); + EXPECT_EQ(rx.Read(kChunkSize).result(), message); user_b.CancelPayload(); absl::Time start_time = SystemClock::ElapsedRealtime(); while (true) { - if (!tx.Write(message).Ok()) break; + if (!tx->Write(message).Ok()) break; absl::Duration run_time = SystemClock::ElapsedRealtime() - start_time; if (run_time >= kLongTimeout) { EXPECT_LT(run_time, kLongTimeout); diff --git a/connections/implementation/payload_manager_test.cc b/connections/implementation/payload_manager_test.cc index 2c2c5f9f..f1984264 100644 --- a/connections/implementation/payload_manager_test.cc +++ b/connections/implementation/payload_manager_test.cc @@ -14,19 +14,29 @@ #include "connections/implementation/payload_manager.h" -#include "gmock/gmock.h" -#include "protobuf-matchers/protocol-buffer-matchers.h" +#include +#include +#include + #include "gtest/gtest.h" #include "absl/strings/string_view.h" +#include "absl/time/time.h" #include "connections/implementation/simulation_user.h" +#include "connections/listeners.h" +#include "connections/medium_selector.h" +#include "connections/payload.h" +#include "connections/status.h" #include "internal/platform/byte_array.h" +#include "internal/platform/count_down_latch.h" +#include "internal/platform/logging.h" +#include "internal/platform/medium_environment.h" #include "internal/platform/pipe.h" -#include "internal/platform/system_clock.h" namespace nearby { namespace connections { namespace { +constexpr size_t kChunkSize = 64 * 1024; constexpr absl::string_view kServiceId = "service-id"; constexpr absl::string_view kDeviceA = "device-a"; constexpr absl::string_view kDeviceB = "device-b"; @@ -165,18 +175,14 @@ TEST_P(PayloadManagerTest, CanSendStreamPayload) { PayloadSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); - auto pipe = std::make_shared(); - OutputStream& tx = pipe->GetOutputStream(); - + auto [input, tx] = CreatePipe(); user_a.ExpectPayload(payload_latch_); const ByteArray message{std::string(kMessage)}; // The first write to the output stream will send the first PAYLOAD_TRANSFER // packet with payload info and message data. - tx.Write(message); + tx->Write(message); - user_b.SendPayload(Payload([pipe]() -> InputStream& { - return pipe->GetInputStream(); // NOLINT - })); + user_b.SendPayload(Payload(std::move(input))); ASSERT_TRUE(payload_latch_.Await(kDefaultTimeout).result()); ASSERT_NE(user_a.GetPayload().AsStream(), nullptr); InputStream& rx = *user_a.GetPayload().AsStream(); @@ -187,22 +193,22 @@ TEST_P(PayloadManagerTest, CanSendStreamPayload) { return info.bytes_transferred >= message.size(); }, kProgressTimeout)); - ByteArray result = rx.Read(Pipe::kChunkSize).result(); + ByteArray result = rx.Read(kChunkSize).result(); EXPECT_EQ(result, message); NEARBY_LOG(INFO, "Packet 1 handled."); - tx.Write(message); + tx->Write(message); EXPECT_TRUE(user_a.WaitForProgress( [&message](const PayloadProgressInfo& info) { return info.bytes_transferred >= 2 * message.size(); }, kProgressTimeout)); - ByteArray result2 = rx.Read(Pipe::kChunkSize).result(); + ByteArray result2 = rx.Read(kChunkSize).result(); EXPECT_EQ(result2, message); NEARBY_LOG(INFO, "Packet 2 handled."); rx.Close(); - tx.Close(); + tx->Close(); NEARBY_LOG(INFO, "Test completed."); user_a.Stop(); user_b.Stop(); @@ -214,17 +220,12 @@ TEST_P(PayloadManagerTest, CanCancelPayloadOnReceiverSide) { PayloadSimulationUser user_a(kDeviceA, GetParam()); PayloadSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); - - auto pipe = std::make_shared(); - OutputStream& tx = pipe->GetOutputStream(); - + auto [input, tx] = CreatePipe(); user_a.ExpectPayload(payload_latch_); const ByteArray message{std::string(kMessage)}; - tx.Write(message); + tx->Write(message); - user_b.SendPayload(Payload([pipe]() -> InputStream& { - return pipe->GetInputStream(); // NOLINT - })); + user_b.SendPayload(Payload(std::move(input))); ASSERT_TRUE(payload_latch_.Await(kDefaultTimeout).result()); ASSERT_NE(user_a.GetPayload().AsStream(), nullptr); InputStream& rx = *user_a.GetPayload().AsStream(); @@ -235,7 +236,7 @@ TEST_P(PayloadManagerTest, CanCancelPayloadOnReceiverSide) { return info.bytes_transferred >= message.size(); }, kProgressTimeout)); - ByteArray result = rx.Read(Pipe::kChunkSize).result(); + ByteArray result = rx.Read(kChunkSize).result(); EXPECT_EQ(result, message); NEARBY_LOG(INFO, "Packet 1 handled."); @@ -246,7 +247,7 @@ TEST_P(PayloadManagerTest, CanCancelPayloadOnReceiverSide) { // Once cancel is handled, write will fail. int count = 0; while (true) { - if (!tx.Write(message).Ok()) break; + if (!tx->Write(message).Ok()) break; SystemClock::Sleep(kDefaultTimeout); count++; } @@ -258,7 +259,7 @@ TEST_P(PayloadManagerTest, CanCancelPayloadOnReceiverSide) { kProgressTimeout)); NEARBY_LOG(INFO, "Stream cancelation received."); - tx.Close(); + tx->Close(); rx.Close(); NEARBY_LOG(INFO, "Test completed."); @@ -272,17 +273,12 @@ TEST_P(PayloadManagerTest, CanCancelPayloadOnSenderSide) { PayloadSimulationUser user_a(kDeviceA, GetParam()); PayloadSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); - - auto pipe = std::make_shared(); - OutputStream& tx = pipe->GetOutputStream(); - + auto [input, tx] = CreatePipe(); user_a.ExpectPayload(payload_latch_); const ByteArray message{std::string(kMessage)}; - tx.Write(message); + tx->Write(message); - user_b.SendPayload(Payload([pipe]() -> InputStream& { - return pipe->GetInputStream(); // NOLINT - })); + user_b.SendPayload(Payload(std::move(input))); ASSERT_TRUE(payload_latch_.Await(kDefaultTimeout).result()); ASSERT_NE(user_a.GetPayload().AsStream(), nullptr); InputStream& rx = *user_a.GetPayload().AsStream(); @@ -293,7 +289,7 @@ TEST_P(PayloadManagerTest, CanCancelPayloadOnSenderSide) { return info.bytes_transferred >= message.size(); }, kProgressTimeout)); - ByteArray result = rx.Read(Pipe::kChunkSize).result(); + ByteArray result = rx.Read(kChunkSize).result(); EXPECT_EQ(result, message); NEARBY_LOG(INFO, "Packet 1 handled."); @@ -304,7 +300,7 @@ TEST_P(PayloadManagerTest, CanCancelPayloadOnSenderSide) { // Once cancel is handled, write will fail. int count = 0; while (true) { - if (!tx.Write(message).Ok()) break; + if (!tx->Write(message).Ok()) break; SystemClock::Sleep(kDefaultTimeout); count++; } @@ -316,7 +312,7 @@ TEST_P(PayloadManagerTest, CanCancelPayloadOnSenderSide) { kProgressTimeout)); NEARBY_LOG(INFO, "Stream cancelation received."); - tx.Close(); + tx->Close(); rx.Close(); NEARBY_LOG(INFO, "Test completed."); @@ -331,19 +327,14 @@ TEST_P(PayloadManagerTest, SendPayloadWithSkip_StreamPayload) { PayloadSimulationUser user_a(kDeviceA, GetParam()); PayloadSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); - - auto pipe = std::make_shared(); - OutputStream& tx = pipe->GetOutputStream(); - + auto [input, tx] = CreatePipe(); user_a.ExpectPayload(payload_latch_); const ByteArray message{std::string(kMessage)}; // The first write to the output stream will send the first PAYLOAD_TRANSFER // packet with payload info and message data. - tx.Write(message); + tx->Write(message); - Payload payload([pipe]() -> InputStream& { - return pipe->GetInputStream(); // NOLINT - }); + Payload payload(std::move(input)); payload.SetOffset(kOffset); user_b.SendPayload(std::move(payload)); ASSERT_TRUE(payload_latch_.Await(kDefaultTimeout).result()); @@ -356,22 +347,22 @@ TEST_P(PayloadManagerTest, SendPayloadWithSkip_StreamPayload) { return info.bytes_transferred >= message.size() - kOffset; }, kProgressTimeout)); - ByteArray result = rx.Read(Pipe::kChunkSize).result(); + ByteArray result = rx.Read(kChunkSize).result(); EXPECT_EQ(result, ByteArray("sage")); NEARBY_LOG(INFO, "Packet 1 handled."); - tx.Write(message); + tx->Write(message); EXPECT_TRUE(user_a.WaitForProgress( [&message](const PayloadProgressInfo& info) { return info.bytes_transferred >= 2 * message.size() - kOffset; }, kProgressTimeout)); - ByteArray result2 = rx.Read(Pipe::kChunkSize).result(); + ByteArray result2 = rx.Read(kChunkSize).result(); EXPECT_EQ(result2, message); NEARBY_LOG(INFO, "Packet 2 handled."); rx.Close(); - tx.Close(); + tx->Close(); NEARBY_LOG(INFO, "Test completed."); user_a.Stop(); user_b.Stop(); diff --git a/connections/payload.cc b/connections/payload.cc index 81cd9015..8b43133f 100644 --- a/connections/payload.cc +++ b/connections/payload.cc @@ -15,7 +15,18 @@ #include "connections/payload.h" #include +#include +#include +#include #include +#include +#include + +#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/prng.h" namespace nearby { namespace connections { @@ -43,8 +54,7 @@ Payload::~Payload() = default; Payload& Payload::operator=(Payload&& other) noexcept = default; // Default (invalid) payload. -Payload::Payload() - : type_(PayloadType::kUnknown), content_(absl::monostate()) {} +Payload::Payload() : type_(PayloadType::kUnknown), content_(std::monostate()) {} // Constructors for outgoing payloads. Payload::Payload(ByteArray&& bytes) @@ -73,7 +83,7 @@ Payload::Payload(std::string parent_folder, std::string file_name, type_(PayloadType::kFile), content_(std::move(input_file)) {} -Payload::Payload(std::function stream) +Payload::Payload(std::unique_ptr stream) : type_(PayloadType::kStream), content_(std::move(stream)) {} // Constructors for incoming payloads. @@ -91,22 +101,23 @@ Payload::Payload(Id id, std::string parent_folder, std::string file_name, type_(PayloadType::kFile), content_(std::move(input_file)) {} -Payload::Payload(Id id, std::function stream) +Payload::Payload(Id id, std::unique_ptr stream) : id_(id), type_(PayloadType::kStream), content_(std::move(stream)) {} // Returns ByteArray payload, if it has been defined, or empty ByteArray. const ByteArray& Payload::AsBytes() const& { static const ByteArray empty; // NOLINT: function-level static is OK. - auto* result = absl::get_if(&content_); + auto* result = std::get_if(&content_); return result ? *result : empty; } // Returns InputStream* payload, if it has been defined, or nullptr. InputStream* Payload::AsStream() { - auto* result = absl::get_if>(&content_); - return result ? &(*result)() : nullptr; + auto* result = std::get_if>(&content_); + return result ? result->get() : nullptr; } + // Returns InputFile* payload, if it has been defined, or nullptr. -InputFile* Payload::AsFile() { return absl::get_if(&content_); } +InputFile* Payload::AsFile() { return std::get_if(&content_); } // Returns Payload unique ID. Payload::Id Payload::GetId() const { return id_; } diff --git a/connections/payload.h b/connections/payload.h index f5a01d42..4c554828 100644 --- a/connections/payload.h +++ b/connections/payload.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "absl/types/variant.h" #include "connections/payload_type.h" @@ -40,8 +41,8 @@ class Payload { using Id = PayloadId; // Order of types in variant, and values in Type enum is important. // Enum values must match respective variant types. - using Content = absl::variant, InputFile>; + using Content = std::variant, InputFile>; Payload(Payload&& other) noexcept; ~Payload(); @@ -71,7 +72,7 @@ class Payload { explicit Payload(std::string parent_folder, std::string file_name, InputFile file); - explicit Payload(std::function stream); + explicit Payload(std::unique_ptr stream); // Constructors for incoming payloads. Payload(Id id, ByteArray&& bytes); @@ -79,7 +80,7 @@ class Payload { Payload(Id id, InputFile file); Payload(Id id, std::string parent_folder, std::string file_name, InputFile input_file); - Payload(Id id, std::function stream); + Payload(Id id, std::unique_ptr stream); // Returns ByteArray payload, if it has been defined, or empty ByteArray. const ByteArray& AsBytes() const&; diff --git a/connections/payload_test.cc b/connections/payload_test.cc index 06d126cd..7a3aad02 100644 --- a/connections/payload_test.cc +++ b/connections/payload_test.cc @@ -16,6 +16,7 @@ #include #include +#include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" @@ -95,19 +96,14 @@ TEST(PayloadTest, TEST(PayloadTest, SupportsStreamType) { constexpr size_t kOffset = 1234456; - auto pipe = std::make_shared(); + auto [input, output] = CreatePipe(); + InputStream* input_stream = input.get(); - Payload payload([streamable = pipe]() -> InputStream& { - // For some reason, linter warns us that we return a dangling reference. - // This is not true: we return a reference to internal variable of a - // shared_ptr which remains valid while Payload is valid, since - // shared_ptr is captured by value. - return streamable->GetInputStream(); // NOLINT - }); + Payload payload(std::move(input)); payload.SetOffset(kOffset); EXPECT_EQ(payload.GetType(), PayloadType::kStream); - EXPECT_EQ(payload.AsStream(), &pipe->GetInputStream()); + EXPECT_EQ(payload.AsStream(), input_stream); EXPECT_EQ(payload.AsFile(), nullptr); EXPECT_EQ(payload.AsBytes(), ByteArray{}); EXPECT_EQ(payload.GetOffset(), kOffset); diff --git a/connections/swift/NearbyCoreAdapter/BUILD b/connections/swift/NearbyCoreAdapter/BUILD index d92ec9c2..e6103c51 100644 --- a/connections/swift/NearbyCoreAdapter/BUILD +++ b/connections/swift/NearbyCoreAdapter/BUILD @@ -36,7 +36,6 @@ objc_library( "//internal/platform:base", "//internal/platform/implementation/apple", # buildcleaner: keep "//third_party/apple_frameworks:Foundation", - "//third_party/apple_frameworks:ObjectiveC", "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger", ], ) diff --git a/connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.h b/connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.h new file mode 100644 index 00000000..67be0b85 --- /dev/null +++ b/connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.h @@ -0,0 +1,45 @@ +// 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. + +// Note: File language is detected using heuristics. Many Objective-C++ headers +// are incorrectly classified as C++ resulting in invalid linter errors. The use +// of "NSArray" and other Foundation classes like "NSData", "NSDictionary" and +// "NSUUID" are highly weighted for Objective-C and Objective-C++ scores. Oddly, +// "#import " does not contribute any points. This +// comment alone should be enough to trick the IDE in to believing this is +// actually some sort of Objective-C file. See: +// cs/google3/devtools/search/lang/recognize_language_classifiers_data + +#import + +#ifdef __cplusplus + +// TODO(b/239758418): Change this to the non-internal version when available. +#include "internal/platform/input_stream.h" + +class CPPInputStream : public nearby::InputStream { + public: + explicit CPPInputStream(NSInputStream *iStream); + + ~CPPInputStream() override; + + nearby::ExceptionOr Read(std::int64_t size) override; + + nearby::Exception Close() override; + + private: + NSInputStream *iStream_; +}; + +#endif diff --git a/connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.mm b/connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.mm new file mode 100644 index 00000000..a3fe9728 --- /dev/null +++ b/connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.mm @@ -0,0 +1,46 @@ +// 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. + +#import "connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.h" + +#import + +#include +#include + +using ::nearby::ByteArray; +using ::nearby::Exception; +using ::nearby::ExceptionOr; + +CPPInputStream::CPPInputStream(NSInputStream *iStream) : iStream_(iStream) { [iStream_ open]; } + +CPPInputStream::~CPPInputStream() { Close(); } + +ExceptionOr CPPInputStream::Read(std::int64_t size) { + std::vector buffer; + buffer.reserve(size); + NSInteger numberOfBytesRead = [iStream_ read:buffer.data() maxLength:size]; + if (numberOfBytesRead == 0) { + return ExceptionOr(); + } + if (numberOfBytesRead < 0) { + return ExceptionOr(Exception::kIo); + } + return ExceptionOr(ByteArray((const char *)buffer.data(), numberOfBytesRead)); +} + +Exception CPPInputStream::Close() { + [iStream_ close]; + return Exception{Exception::kSuccess}; +} diff --git a/connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.h b/connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.h deleted file mode 100644 index 06aac0d9..00000000 --- a/connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.h +++ /dev/null @@ -1,50 +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. - -#import - -#ifdef __cplusplus - -namespace nearby { -class InputStream; -} - -#endif - -@interface CPPInputStreamBinding : NSObject - -/** - * Creates and attaches a @c CPPInputStreamBinding object to the provided stream as an associated - * object. - * - * This association makes it possible for a c++ pointer to live as long as the original user - * provided @c NSInputStream object. - * - * @param stream The stream to become associated with. - */ -+ (void)bindToStream:(NSInputStream *)stream; - -#ifdef __cplusplus -/** - * Retreives a reference to the c++ pointer associated to the provided stream. - * - * CPPInputStreamBinding::bindToStream: must be called on the this stream before calling this - * function. - * - * @param stream The stream that has a c++ pointer associated with it. - */ -+ (nearby::InputStream &)getRefFromStream:(NSInputStream *)stream; -#endif - -@end diff --git a/connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.mm b/connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.mm deleted file mode 100644 index d546ba5c..00000000 --- a/connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.mm +++ /dev/null @@ -1,90 +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. - -#import "connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.h" - -#import -#import - -#include -#include - -// TODO(b/239758418): Change this to the non-internal version when available. -#include "internal/platform/input_stream.h" - -using ::nearby::ByteArray; -using ::nearby::Exception; -using ::nearby::ExceptionOr; -using ::nearby::InputStream; - -class CPPInputStream : public InputStream { - public: - explicit CPPInputStream(NSInputStream *iStream) : iStream_(iStream) { [iStream_ open]; } - - ~CPPInputStream() override { Close(); } - - ExceptionOr Read(std::int64_t size) override { - std::vector buffer; - buffer.reserve(size); - NSInteger numberOfBytesRead = [iStream_ read:buffer.data() maxLength:size]; - if (numberOfBytesRead == 0) { - return ExceptionOr(); - } - if (numberOfBytesRead < 0) { - return ExceptionOr(Exception::kIo); - } - return ExceptionOr(ByteArray((const char *)buffer.data(), numberOfBytesRead)); - } - - Exception Close() override { - [iStream_ close]; - return Exception{Exception::kSuccess}; - } - - private: - // Prevent a retain cycle since NSInputStream will have a strong reference to this object. - __weak NSInputStream *iStream_; -}; - -// Wrap a c++ InputStream subclass in objective-c so it can be associated with the original -// NSInputStream object. The association makes it possible for the unique_ptr to live as long as -// the original user provided NSInputStream. -@implementation CPPInputStreamBinding { - @public - std::unique_ptr _cppStream; -} - -// This field's address is used as a unique identifier. -static char gAssociatedStreamKey; - -- (instancetype)initWithStream:(NSInputStream *)stream { - self = [super init]; - if (self) { - _cppStream = std::make_unique(stream); - } - return self; -} - -+ (void)bindToStream:(NSInputStream *)stream { - CPPInputStreamBinding *binding = [[CPPInputStreamBinding alloc] initWithStream:stream]; - objc_setAssociatedObject(stream, &gAssociatedStreamKey, binding, - OBJC_ASSOCIATION_RETAIN_NONATOMIC); -} - -+ (InputStream &)getRefFromStream:(NSInputStream *)stream { - CPPInputStreamBinding *binding = objc_getAssociatedObject(stream, &gAssociatedStreamKey); - return *binding->_cppStream; -} - -@end diff --git a/connections/swift/NearbyCoreAdapter/Sources/GNCInputStream.h b/connections/swift/NearbyCoreAdapter/Sources/GNCInputStream.h index bdd698e1..151a1a52 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/GNCInputStream.h +++ b/connections/swift/NearbyCoreAdapter/Sources/GNCInputStream.h @@ -17,8 +17,10 @@ #ifdef __cplusplus namespace nearby { -class InputStream; +namespace connections { +class Payload; } +} // namespace nearby #endif @@ -29,7 +31,7 @@ class InputStream; - (nonnull instancetype)initWithURL:(nonnull NSURL *)url NS_UNAVAILABLE; #ifdef __cplusplus -- (nonnull instancetype)initWithCppInputStream:(nonnull nearby::InputStream *)stream +- (nonnull instancetype)initWithPayload:(nearby::connections::Payload)payload NS_DESIGNATED_INITIALIZER; #endif diff --git a/connections/swift/NearbyCoreAdapter/Sources/GNCInputStream.mm b/connections/swift/NearbyCoreAdapter/Sources/GNCInputStream.mm index d4c3096f..2b697a5f 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/GNCInputStream.mm +++ b/connections/swift/NearbyCoreAdapter/Sources/GNCInputStream.mm @@ -18,31 +18,30 @@ #include -// TODO(b/239758418): Change this to the non-internal version when available. -#include "internal/platform/input_stream.h" +#include "connections/payload.h" #import "connections/swift/NearbyCoreAdapter/Sources/GNCException+Internal.h" #import "connections/swift/NearbyCoreAdapter/Sources/Public/NearbyCoreAdapter/GNCException.h" using ::nearby::ByteArray; using ::nearby::ExceptionOr; -using ::nearby::InputStream; using ::nearby::connections::NSErrorFromCppException; +using ::nearby::connections::Payload; @implementation GNCInputStream { NSStreamStatus _streamStatus; NSError *_streamError; id _delegate; - InputStream *_stream; + Payload _payload; } -- (instancetype)initWithCppInputStream:(InputStream *)stream { +- (instancetype)initWithPayload:(Payload)payload { // Init with empty data because init is not a designated initializer. self = [super initWithData:[[NSData alloc] init]]; if (self) { _streamStatus = NSStreamStatusNotOpen; _delegate = self; - _stream = stream; + _payload = std::move(payload); } return self; @@ -53,7 +52,7 @@ using ::nearby::connections::NSErrorFromCppException; } - (NSInteger)read:(uint8_t *)buffer maxLength:(NSUInteger)maxLen { - ExceptionOr readResult = _stream->Read(maxLen); + ExceptionOr readResult = _payload.AsStream()->Read(maxLen); if (!readResult.ok()) { _streamError = NSErrorFromCppException(readResult.GetException()); @@ -87,7 +86,7 @@ using ::nearby::connections::NSErrorFromCppException; - (void)close { _streamStatus = NSStreamStatusClosed; - _stream->Close(); + _payload.AsStream()->Close(); } - (NSStreamStatus)streamStatus { diff --git a/connections/swift/NearbyCoreAdapter/Sources/GNCPayload+CppConversions.h b/connections/swift/NearbyCoreAdapter/Sources/GNCPayload+CppConversions.h index 7d265e97..97e54e1f 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/GNCPayload+CppConversions.h +++ b/connections/swift/NearbyCoreAdapter/Sources/GNCPayload+CppConversions.h @@ -29,10 +29,20 @@ class Payload; @interface GNCPayload (CppConversions) #ifdef __cplusplus +/** + * @note @c fromCpp should not be used to convert a @c Payload created from @c toCpp. For some + * payload types, each conversion creates a new object holding a reference to the previous, + * resulting in potentially endless nesting of objects. + */ + (nonnull GNCPayload *)fromCpp:(nearby::connections::Payload)payload; #endif #ifdef __cplusplus +/** + * @note @c toCPP should not be used to convert a @c GNCPayload created from @c fromCpp. For some + * payload types, each conversion creates a new object holding a reference to the previous, + * resulting in potentially endless nesting of objects. + */ - (nearby::connections::Payload)toCpp; #endif diff --git a/connections/swift/NearbyCoreAdapter/Sources/GNCPayload+CppConversions.mm b/connections/swift/NearbyCoreAdapter/Sources/GNCPayload+CppConversions.mm index 71956a9b..6cc8aa4b 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/GNCPayload+CppConversions.mm +++ b/connections/swift/NearbyCoreAdapter/Sources/GNCPayload+CppConversions.mm @@ -20,13 +20,12 @@ #include "connections/payload.h" -#import "connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.h" +#import "connections/swift/NearbyCoreAdapter/Sources/CPPInputStream.h" #import "connections/swift/NearbyCoreAdapter/Sources/GNCInputStream.h" #import "connections/swift/NearbyCoreAdapter/Sources/GNCPayload+CppConversions.h" using ::nearby::ByteArray; using ::nearby::InputFile; -using ::nearby::InputStream; using ::nearby::connections::Payload; @implementation GNCPayload (CppConversions) @@ -51,7 +50,7 @@ using ::nearby::connections::Payload; identifier:payloadId]; } case nearby::connections::PayloadType::kStream: { - GNCInputStream *stream = [[GNCInputStream alloc] initWithCppInputStream:payload.AsStream()]; + GNCInputStream *stream = [[GNCInputStream alloc] initWithPayload:std::move(payload)]; return [[GNCStreamPayload alloc] initWithStream:stream identifier:payloadId]; } case nearby::connections::PayloadType::kUnknown: @@ -76,14 +75,7 @@ using ::nearby::connections::Payload; @implementation GNCStreamPayload (CppConversions) - (Payload)toCpp { - // GNCStreamPayload will most likely be destroyed almost immediately, so a weak self would be - // useless and a strong self will cause a retain cycle. This is why we are keeping a weak - // reference of the stream instead. The input stream should be kept alive by a strong reference - // on the user end. - __weak NSInputStream *stream = self.stream; - return Payload(self.identifier, [stream]() -> InputStream & { - return [CPPInputStreamBinding getRefFromStream:stream]; - }); + return Payload(self.identifier, std::make_unique(self.stream)); } @end diff --git a/connections/swift/NearbyCoreAdapter/Sources/GNCPayload.mm b/connections/swift/NearbyCoreAdapter/Sources/GNCPayload.mm index 7c8415a2..58d6c032 100644 --- a/connections/swift/NearbyCoreAdapter/Sources/GNCPayload.mm +++ b/connections/swift/NearbyCoreAdapter/Sources/GNCPayload.mm @@ -20,8 +20,6 @@ #include "connections/payload.h" -#import "connections/swift/NearbyCoreAdapter/Sources/CPPInputStreamBinding.h" - using ::nearby::connections::Payload; @implementation GNCPayload @@ -62,7 +60,6 @@ using ::nearby::connections::Payload; self = [super initWithIdentifier:identifier]; if (self) { _stream = stream; - [CPPInputStreamBinding bindToStream:stream]; } return self; } diff --git a/internal/platform/BUILD b/internal/platform/BUILD index e839fdbd..20c15bfd 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -73,13 +73,11 @@ cc_library( name = "util", srcs = [ "base_input_stream.cc", - "base_pipe.cc", "byte_utils.cc", ], hdrs = [ "base_input_stream.h", "base_mutex_lock.h", - "base_pipe.h", "byte_utils.h", ], visibility = [ @@ -465,6 +463,7 @@ cc_test( shard_count = 16, deps = [ ":base", + ":cancellation_flag", ":comm", ":connection_info", ":test_util", diff --git a/internal/platform/base_pipe.cc b/internal/platform/base_pipe.cc deleted file mode 100644 index 94c6b917..00000000 --- a/internal/platform/base_pipe.cc +++ /dev/null @@ -1,103 +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 "internal/platform/base_pipe.h" - -#include "internal/platform/base_mutex_lock.h" -#include "internal/platform/input_stream.h" -#include "internal/platform/output_stream.h" - -namespace nearby { - -ExceptionOr BasePipe::Read(size_t size) { - BaseMutexLock lock(mutex_.get()); - - // We're done reading all the chunks that were written before the OutputStream - // was closed, so there's nothing to do here other than return an empty chunk - // to serve as an EOF indication to callers. - if (read_all_chunks_) { - return ExceptionOr{ByteArray{}}; - } - - while (buffer_.empty() && !input_stream_closed_) { - Exception wait_exception = cond_->Wait(); - - if (wait_exception.Raised()) { - return ExceptionOr{wait_exception}; - } - } - - // If we received our sentinel chunk, mark the fact that there cannot - // possibly be any more chunks to read here on in, and return an empty chunk - // to serve as an EOF indication to callers. - if (buffer_.empty() || buffer_.front().Empty()) { - read_all_chunks_ = true; - return ExceptionOr{ByteArray{}}; - } - - ByteArray first_chunk{buffer_.front()}; - buffer_.pop_front(); - - // If first_chunk is small enough to not overshoot the requested 'size', just - // return that. - if (first_chunk.size() <= size) { - return ExceptionOr{first_chunk}; - } else { - // Break first_chunk into 2 parts -- the first one of which (next_chunk) - // will be 'size' bytes long, and will be returned, and the second one of - // which (overflow_chunk) will be re-inserted into buffer_, at the head of - // the queue, to be served up in the next call to read(). - ByteArray next_chunk(first_chunk.data(), size); - buffer_.push_front( - ByteArray(first_chunk.data() + size, first_chunk.size() - size)); - return ExceptionOr{next_chunk}; - } -} - -Exception BasePipe::Write(const ByteArray& data) { - BaseMutexLock lock(mutex_.get()); - - return WriteLocked(data); -} - -void BasePipe::MarkInputStreamClosed() { - BaseMutexLock lock(mutex_.get()); - - input_stream_closed_ = true; - // Trigger cond_ to unblock a potentially-blocked call to read(), and to let - // it know to return Exception::IO. - cond_->Notify(); -} - -void BasePipe::MarkOutputStreamClosed() { - BaseMutexLock lock(mutex_.get()); - - // Write a sentinel null chunk before marking output_stream_closed as true. - WriteLocked(ByteArray{}); - output_stream_closed_ = true; -} - -Exception BasePipe::WriteLocked(const ByteArray& data) { - if (input_stream_closed_ || output_stream_closed_) { - return {Exception::kIo}; - } - - buffer_.push_back(data); - // Trigger cond_ to unblock a potentially-blocked call to read(), now that - // there's more data for it to consume. - cond_->Notify(); - return {Exception::kSuccess}; -} - -} // namespace nearby diff --git a/internal/platform/base_pipe.h b/internal/platform/base_pipe.h deleted file mode 100644 index 3b8987de..00000000 --- a/internal/platform/base_pipe.h +++ /dev/null @@ -1,136 +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_BASE_BASE_PIPE_H_ -#define PLATFORM_BASE_BASE_PIPE_H_ - -#include -#include -#include - -#include "absl/base/thread_annotations.h" -#include "internal/platform/implementation/condition_variable.h" -#include "internal/platform/implementation/mutex.h" -#include "internal/platform/byte_array.h" -#include "internal/platform/exception.h" -#include "internal/platform/input_stream.h" -#include "internal/platform/output_stream.h" - -namespace nearby { - -// Common Pipe implementation. -// It does not depend on platform implementation, and this allows it to -// be used in the platform implementation itself. -// Concrete class must be derived from it, as follows: -// -// class DerivedPipe : public BasePipe { -// public: -// DerivedPipe() { -// auto mutex = /* construct platform-dependent mutex */; -// auto cond = /* construct platform-dependent condition variable */; -// Setup(std::move(mutex), std::move(cond)); -// } -// ~DerivedPipe() override = default; -// DerivedPipe(DerivedPipe&&) = default; -// DerivedPipe& operator=(DerivedPipe&&) = default; -// }; -class BasePipe { - public: - static constexpr const size_t kChunkSize = 64 * 1024; - virtual ~BasePipe() = default; - - // Pipe is not copyable or movable, because copy/move will invalidate - // references to input and output streams. - // If move is required, Pipe could be wrapped with std::unique_ptr<>. - BasePipe(BasePipe&&) = delete; - BasePipe& operator=(BasePipe&&) = delete; - - // Get...() methods return references to input and output steam facades. - // It is safe to call Get...() methods multiple times. - InputStream& GetInputStream() { return input_stream_; } - OutputStream& GetOutputStream() { return output_stream_; } - - protected: - BasePipe() = default; - - void Setup(std::unique_ptr mutex, - std::unique_ptr cond) { - mutex_ = std::move(mutex); - cond_ = std::move(cond); - } - - private: - class BasePipeInputStream : public InputStream { - public: - explicit BasePipeInputStream(BasePipe* pipe) : pipe_(pipe) {} - ~BasePipeInputStream() override { DoClose(); } - - ExceptionOr Read(std::int64_t size) override { - return pipe_->Read(size); - } - Exception Close() override { return DoClose(); } - - private: - Exception DoClose() { - pipe_->MarkInputStreamClosed(); - return {Exception::kSuccess}; - } - BasePipe* pipe_; - }; - class BasePipeOutputStream : public OutputStream { - public: - explicit BasePipeOutputStream(BasePipe* pipe) : pipe_(pipe) {} - ~BasePipeOutputStream() override { DoClose(); } - - Exception Write(const ByteArray& data) override { - return pipe_->Write(data); - } - Exception Flush() override { return {Exception::kSuccess}; } - Exception Close() override { return DoClose(); } - - private: - Exception DoClose() { - pipe_->MarkOutputStreamClosed(); - return {Exception::kSuccess}; - } - BasePipe* pipe_; - }; - - ExceptionOr Read(size_t size) ABSL_LOCKS_EXCLUDED(mutex_); - Exception Write(const ByteArray& data) ABSL_LOCKS_EXCLUDED(mutex_); - - void MarkInputStreamClosed() ABSL_LOCKS_EXCLUDED(mutex_); - void MarkOutputStreamClosed() ABSL_LOCKS_EXCLUDED(mutex_); - - Exception WriteLocked(const ByteArray& data) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - // Order of declaration matters: - // - mutex must be defined before condvar; - // - input & output streams must be after both mutex and condvar. - bool input_stream_closed_ ABSL_GUARDED_BY(mutex_) = false; - bool output_stream_closed_ ABSL_GUARDED_BY(mutex_) = false; - bool read_all_chunks_ ABSL_GUARDED_BY(mutex_) = false; - - std::deque ABSL_GUARDED_BY(mutex_) buffer_; - std::unique_ptr mutex_; - std::unique_ptr cond_; - - BasePipeInputStream input_stream_{this}; - BasePipeOutputStream output_stream_{this}; -}; - -} // namespace nearby - -#endif // PLATFORM_BASE_BASE_PIPE_H_ diff --git a/internal/platform/bluetooth_classic_test.cc b/internal/platform/bluetooth_classic_test.cc index 12d97989..3eabdb03 100644 --- a/internal/platform/bluetooth_classic_test.cc +++ b/internal/platform/bluetooth_classic_test.cc @@ -18,12 +18,16 @@ #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/time.h" #include "internal/platform/bluetooth_adapter.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/exception.h" +#include "internal/platform/feature_flags.h" +#include "internal/platform/implementation/bluetooth_adapter.h" #include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" @@ -270,7 +274,7 @@ TEST_F(BluetoothClassicMediumTest, SendData) { server_socket.Close(); } -TEST_F(BluetoothClassicMediumTest, IoOnClosedSocketReturnsError) { +TEST_F(BluetoothClassicMediumTest, IoOnClosedSocketReturnsEmpty) { adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); CountDownLatch found_latch(1); BluetoothDevice* discovered_device = nullptr; @@ -308,7 +312,7 @@ TEST_F(BluetoothClassicMediumTest, IoOnClosedSocketReturnsError) { BluetoothSocket socket_b = server_socket.Accept(); ASSERT_TRUE(socket_b.IsValid()); socket_b.Close(); - EXPECT_FALSE(socket_b.GetInputStream().Read(data.size()).ok()); + EXPECT_TRUE(socket_b.GetInputStream().Read(data.size()).result().Empty()); }); } server_socket.Close(); diff --git a/internal/platform/implementation/g3/BUILD b/internal/platform/implementation/g3/BUILD index 02f45c6c..9a4359d6 100644 --- a/internal/platform/implementation/g3/BUILD +++ b/internal/platform/implementation/g3/BUILD @@ -30,7 +30,6 @@ cc_library( "log_message.h", "multi_thread_executor.h", "mutex.h", - "pipe.h", "preferences_manager.h", "scheduled_executor.h", "single_thread_executor.h", @@ -98,7 +97,10 @@ cc_library( "@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/log:check", "@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", diff --git a/internal/platform/implementation/g3/ble.cc b/internal/platform/implementation/g3/ble.cc index 310d19be..f60b2391 100644 --- a/internal/platform/implementation/g3/ble.cc +++ b/internal/platform/implementation/g3/ble.cc @@ -17,10 +17,18 @@ #include #include #include +#include +#include "absl/functional/any_invocable.h" +#include "absl/log/check.h" #include "absl/synchronization/mutex.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/cancellation_flag_listener.h" +#include "internal/platform/exception.h" #include "internal/platform/implementation/ble.h" +#include "internal/platform/implementation/bluetooth_adapter.h" +#include "internal/platform/implementation/g3/bluetooth_adapter.h" #include "internal/platform/implementation/shared/count_down_latch.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" diff --git a/internal/platform/implementation/g3/ble.h b/internal/platform/implementation/g3/ble.h index 78736ef8..243468ff 100644 --- a/internal/platform/implementation/g3/ble.h +++ b/internal/platform/implementation/g3/ble.h @@ -27,7 +27,6 @@ #include "internal/platform/implementation/g3/bluetooth_adapter.h" #include "internal/platform/implementation/g3/bluetooth_classic.h" #include "internal/platform/implementation/g3/multi_thread_executor.h" -#include "internal/platform/implementation/g3/pipe.h" #include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/input_stream.h" #include "internal/platform/output_stream.h" diff --git a/internal/platform/implementation/g3/ble_v2.cc b/internal/platform/implementation/g3/ble_v2.cc index f0fcf339..dce7d554 100644 --- a/internal/platform/implementation/g3/ble_v2.cc +++ b/internal/platform/implementation/g3/ble_v2.cc @@ -15,7 +15,6 @@ #include "internal/platform/implementation/g3/ble_v2.h" #include -#include #include #include #include @@ -23,15 +22,26 @@ #include #include +#include "absl/functional/any_invocable.h" +#include "absl/log/check.h" #include "absl/status/status.h" +#include "absl/status/statusor.h" #include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "internal/platform/borrowable.h" +#include "internal/platform/byte_array.h" #include "internal/platform/cancellation_flag_listener.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/exception.h" #include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/bluetooth_adapter.h" #include "internal/platform/implementation/g3/bluetooth_adapter.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" +#include "internal/platform/prng.h" +#include "internal/platform/uuid.h" namespace nearby { namespace g3 { diff --git a/internal/platform/implementation/g3/ble_v2.h b/internal/platform/implementation/g3/ble_v2.h index 4b273a46..2165c014 100644 --- a/internal/platform/implementation/g3/ble_v2.h +++ b/internal/platform/implementation/g3/ble_v2.h @@ -29,7 +29,6 @@ #include "internal/platform/byte_array.h" #include "internal/platform/implementation/ble_v2.h" #include "internal/platform/implementation/g3/bluetooth_adapter.h" -#include "internal/platform/implementation/g3/pipe.h" #include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/medium_environment.h" #include "internal/platform/prng.h" diff --git a/internal/platform/implementation/g3/bluetooth_classic.cc b/internal/platform/implementation/g3/bluetooth_classic.cc index d7a1a832..a4d7b121 100644 --- a/internal/platform/implementation/g3/bluetooth_classic.cc +++ b/internal/platform/implementation/g3/bluetooth_classic.cc @@ -17,12 +17,18 @@ #include #include #include +#include +#include "absl/functional/any_invocable.h" +#include "absl/log/check.h" +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/cancellation_flag_listener.h" +#include "internal/platform/exception.h" +#include "internal/platform/implementation/bluetooth_adapter.h" #include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/implementation/g3/bluetooth_adapter.h" -#include "internal/platform/input_stream.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" diff --git a/internal/platform/implementation/g3/bluetooth_classic.h b/internal/platform/implementation/g3/bluetooth_classic.h index 6bae1fa3..9c4d4257 100644 --- a/internal/platform/implementation/g3/bluetooth_classic.h +++ b/internal/platform/implementation/g3/bluetooth_classic.h @@ -26,7 +26,6 @@ #include "internal/platform/exception.h" #include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/implementation/g3/bluetooth_adapter.h" -#include "internal/platform/implementation/g3/pipe.h" #include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/input_stream.h" #include "internal/platform/listeners.h" diff --git a/internal/platform/implementation/g3/pipe.h b/internal/platform/implementation/g3/pipe.h deleted file mode 100644 index 3c2f7f24..00000000 --- a/internal/platform/implementation/g3/pipe.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 PLATFORM_IMPL_G3_PIPE_H_ -#define PLATFORM_IMPL_G3_PIPE_H_ - -#include - -#include "internal/platform/base_pipe.h" -#include "internal/platform/implementation/g3/condition_variable.h" -#include "internal/platform/implementation/g3/mutex.h" - -namespace nearby { -namespace g3 { - -class Pipe : public BasePipe { - public: - Pipe() { - auto mutex = std::make_unique(/*check=*/true); - auto cond = std::make_unique(mutex.get()); - Setup(std::move(mutex), std::move(cond)); - } - ~Pipe() override = default; - Pipe(Pipe&&) = delete; - Pipe& operator=(Pipe&&) = delete; -}; - -} // namespace g3 -} // namespace nearby - -#endif // PLATFORM_IMPL_G3_PIPE_H_ diff --git a/internal/platform/implementation/g3/socket_base.h b/internal/platform/implementation/g3/socket_base.h index 4da305b9..7678a9e6 100644 --- a/internal/platform/implementation/g3/socket_base.h +++ b/internal/platform/implementation/g3/socket_base.h @@ -18,14 +18,16 @@ #include #include #include +#include +#include #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" #include "internal/platform/byte_array.h" #include "internal/platform/exception.h" -#include "internal/platform/implementation/g3/pipe.h" #include "internal/platform/input_stream.h" #include "internal/platform/output_stream.h" +#include "internal/platform/pipe.h" namespace nearby { namespace g3 { @@ -33,6 +35,7 @@ namespace g3 { // Common base for BT, BLE and Wifi socket implementations. class SocketBase { public: + SocketBase() { std::tie(input_for_remote_, output_) = CreatePipe(); } virtual ~SocketBase() { absl::MutexLock lock(&mutex_); DoClose(); @@ -43,23 +46,17 @@ class SocketBase { void Connect(SocketBase& other) ABSL_LOCKS_EXCLUDED(mutex_) { absl::MutexLock lock(&mutex_); remote_socket_ = &other; - input_ = other.output_; + input_ = std::move(other.input_for_remote_); } // Returns the InputStream of this connected socket. - InputStream& GetInputStream() ABSL_LOCKS_EXCLUDED(mutex_) { - absl::MutexLock lock(&mutex_); - if (IsConnectedLocked()) { - return input_->GetInputStream(); - } - return invalid_input_stream_; - } + InputStream& GetInputStream() { return input_proxy_; } // Returns the OutputStream of this connected socket. // This stream is for local side to write. OutputStream& GetOutputStream() ABSL_LOCKS_EXCLUDED(mutex_) { absl::MutexLock lock(&mutex_); - return output_->GetOutputStream(); + return *output_; } // Returns true if connection exists to the (possibly closed) remote socket. @@ -95,13 +92,15 @@ class SocketBase { void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_) { if (!closed_) { remote_socket_ = nullptr; - output_->GetOutputStream().Close(); - output_->GetInputStream().Close(); - if (IsConnectedLocked()) { - input_->GetOutputStream().Close(); - input_->GetInputStream().Close(); - input_.reset(); + // The client can hold references to `output_` and `input_` streams. We + // can close them but we cannot destroy them. + output_->Close(); + if (input_) { + input_->Close(); } + // The client does not hold a reference to `input_for_remote_`, so we can + // destroy it. Connecting to this socket will fail after that. + input_for_remote_.reset(); closed_ = true; } } @@ -111,24 +110,42 @@ class SocketBase { return input_ != nullptr; } - class InvalidInputStream : public InputStream { + class InputProxyStream : public InputStream { public: + explicit InputProxyStream(SocketBase* socket) : socket_(socket) {} ExceptionOr Read(std::int64_t size) override { - return ExceptionOr(Exception::kIo); + if (!socket_->IsConnected()) { + return ExceptionOr(Exception::kIo); + } + return socket_->input_->Read(size); } ExceptionOr Skip(size_t offset) override { - return ExceptionOr(Exception::kIo); + if (!socket_->IsConnected()) { + return ExceptionOr(Exception::kIo); + } + return socket_->input_->Skip(offset); + } + Exception Close() override { + if (!socket_->IsConnected()) { + return {Exception::kIo}; + } + return socket_->input_->Close(); } - Exception Close() override { return {Exception::kIo}; } - }; - // Returned to the caller if the remote socket is destroyed. - InvalidInputStream invalid_input_stream_; - // Output pipe is initialized by constructor, it remains always valid, until - // it is closed. it represents output part of a local socket. Input part of a - // local socket comes from the peer socket, after connection. - std::shared_ptr output_{new Pipe}; - std::shared_ptr input_; + private: + SocketBase* socket_; + }; + InputProxyStream input_proxy_{this}; + + // Output stream is initialized by constructor, it remains always valid. It + // represents output part of a local socket. Input stream of a local socket + // comes from the peer socket, after connection. + std::unique_ptr output_; + std::unique_ptr input_; + // `input_for_remote_` is the other end of the pipe formed with `output_`. We + // give this stream to the remote socket when they connect to us, and it + // becomes their `input_` stream. + std::unique_ptr input_for_remote_; SocketBase* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; bool closed_ ABSL_GUARDED_BY(mutex_) = false; }; diff --git a/internal/platform/implementation/g3/wifi_direct.cc b/internal/platform/implementation/g3/wifi_direct.cc index d4d97261..8e49f7f4 100644 --- a/internal/platform/implementation/g3/wifi_direct.cc +++ b/internal/platform/implementation/g3/wifi_direct.cc @@ -16,15 +16,21 @@ #include #include -#include #include #include +#include "absl/log/check.h" +#include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/exception.h" #include "internal/platform/implementation/wifi_direct.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" +#include "internal/platform/prng.h" +#include "internal/platform/wifi_credential.h" namespace nearby { namespace g3 { diff --git a/internal/platform/implementation/g3/wifi_direct.h b/internal/platform/implementation/g3/wifi_direct.h index fba77915..d4b07504 100644 --- a/internal/platform/implementation/g3/wifi_direct.h +++ b/internal/platform/implementation/g3/wifi_direct.h @@ -22,7 +22,6 @@ #include "absl/synchronization/mutex.h" #include "internal/platform/implementation/g3/multi_thread_executor.h" -#include "internal/platform/implementation/g3/pipe.h" #include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/implementation/wifi_direct.h" #include "internal/platform/input_stream.h" diff --git a/internal/platform/implementation/g3/wifi_hotspot.cc b/internal/platform/implementation/g3/wifi_hotspot.cc index d4ab753d..b8da8859 100644 --- a/internal/platform/implementation/g3/wifi_hotspot.cc +++ b/internal/platform/implementation/g3/wifi_hotspot.cc @@ -16,16 +16,22 @@ #include #include -#include #include #include +#include "absl/log/check.h" +#include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/cancellation_flag_listener.h" +#include "internal/platform/exception.h" #include "internal/platform/implementation/wifi_hotspot.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" +#include "internal/platform/prng.h" +#include "internal/platform/wifi_credential.h" namespace nearby { namespace g3 { diff --git a/internal/platform/implementation/g3/wifi_hotspot.h b/internal/platform/implementation/g3/wifi_hotspot.h index 6c248dfd..a3b3e69f 100644 --- a/internal/platform/implementation/g3/wifi_hotspot.h +++ b/internal/platform/implementation/g3/wifi_hotspot.h @@ -23,7 +23,6 @@ #include "absl/synchronization/mutex.h" #include "internal/platform/byte_array.h" #include "internal/platform/implementation/g3/multi_thread_executor.h" -#include "internal/platform/implementation/g3/pipe.h" #include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/implementation/wifi_hotspot.h" #include "internal/platform/input_stream.h" diff --git a/internal/platform/implementation/g3/wifi_lan.cc b/internal/platform/implementation/g3/wifi_lan.cc index 078c67b3..8d4626e3 100644 --- a/internal/platform/implementation/g3/wifi_lan.cc +++ b/internal/platform/implementation/g3/wifi_lan.cc @@ -19,10 +19,13 @@ #include #include -#include "absl/strings/escaping.h" +#include "absl/log/check.h" +#include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/synchronization/mutex.h" +#include "internal/platform/cancellation_flag.h" #include "internal/platform/cancellation_flag_listener.h" +#include "internal/platform/exception.h" #include "internal/platform/implementation/wifi_lan.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" diff --git a/internal/platform/implementation/g3/wifi_lan.h b/internal/platform/implementation/g3/wifi_lan.h index 19cb18d3..b51f05fd 100644 --- a/internal/platform/implementation/g3/wifi_lan.h +++ b/internal/platform/implementation/g3/wifi_lan.h @@ -24,7 +24,6 @@ #include "absl/synchronization/mutex.h" #include "internal/platform/byte_array.h" #include "internal/platform/implementation/g3/multi_thread_executor.h" -#include "internal/platform/implementation/g3/pipe.h" #include "internal/platform/implementation/g3/socket_base.h" #include "internal/platform/implementation/wifi_lan.h" #include "internal/platform/input_stream.h" diff --git a/internal/platform/pipe.cc b/internal/platform/pipe.cc index 25a0b9e9..8249686d 100644 --- a/internal/platform/pipe.cc +++ b/internal/platform/pipe.cc @@ -14,25 +14,181 @@ #include "internal/platform/pipe.h" +#include +#include +#include +#include +#include + +#include "absl/base/thread_annotations.h" +#include "internal/platform/base_mutex_lock.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/exception.h" #include "internal/platform/implementation/condition_variable.h" #include "internal/platform/implementation/mutex.h" #include "internal/platform/implementation/platform.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/output_stream.h" namespace nearby { namespace { using Platform = api::ImplementationPlatform; -} +class Pipe { + public: + Pipe() { #pragma push_macro("CreateMutex") #undef CreateMutex + mutex_ = Platform::CreateMutex(api::Mutex::Mode::kRegular); +#pragma pop_macro("CreateMutex") + cond_ = Platform::CreateConditionVariable(mutex_.get()); + } -Pipe::Pipe() { - auto mutex = Platform::CreateMutex(api::Mutex::Mode::kRegular); - auto cond = Platform::CreateConditionVariable(mutex.get()); - Setup(std::move(mutex), std::move(cond)); + class PipeInputStream : public InputStream { + public: + explicit PipeInputStream(std::shared_ptr pipe) : pipe_(pipe) {} + ~PipeInputStream() override { DoClose(); } + + ExceptionOr Read(std::int64_t size) override { + return pipe_->Read(size); + } + Exception Close() override { return DoClose(); } + + private: + Exception DoClose() { + pipe_->MarkInputStreamClosed(); + return {Exception::kSuccess}; + } + std::shared_ptr pipe_; + }; + + class PipeOutputStream : public OutputStream { + public: + explicit PipeOutputStream(std::shared_ptr pipe) : pipe_(pipe) {} + ~PipeOutputStream() override { DoClose(); } + + Exception Write(const ByteArray& data) override { + return pipe_->Write(data); + } + Exception Flush() override { return {Exception::kSuccess}; } + Exception Close() override { return DoClose(); } + + private: + Exception DoClose() { + pipe_->MarkOutputStreamClosed(); + return {Exception::kSuccess}; + } + std::shared_ptr pipe_; + }; + + private: + ExceptionOr Read(size_t size) ABSL_LOCKS_EXCLUDED(mutex_); + Exception Write(const ByteArray& data) ABSL_LOCKS_EXCLUDED(mutex_); + + void MarkInputStreamClosed() ABSL_LOCKS_EXCLUDED(mutex_); + void MarkOutputStreamClosed() ABSL_LOCKS_EXCLUDED(mutex_); + + Exception WriteLocked(const ByteArray& data) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + bool input_stream_closed_ ABSL_GUARDED_BY(mutex_) = false; + bool output_stream_closed_ ABSL_GUARDED_BY(mutex_) = false; + bool read_all_chunks_ ABSL_GUARDED_BY(mutex_) = false; + + std::deque ABSL_GUARDED_BY(mutex_) buffer_; + // Order of declaration matters: + // - mutex must be defined before condvar; + std::unique_ptr mutex_; + std::unique_ptr cond_; +}; + +ExceptionOr Pipe::Read(size_t size) { + BaseMutexLock lock(mutex_.get()); + + // We're done reading all the chunks that were written before the OutputStream + // was closed, so there's nothing to do here other than return an empty chunk + // to serve as an EOF indication to callers. + if (read_all_chunks_) { + return ExceptionOr{ByteArray{}}; + } + + while (buffer_.empty() && !input_stream_closed_) { + Exception wait_exception = cond_->Wait(); + + if (wait_exception.Raised()) { + return ExceptionOr{wait_exception}; + } + } + + // If we received our sentinel chunk, mark the fact that there cannot + // possibly be any more chunks to read here on in, and return an empty chunk + // to serve as an EOF indication to callers. + if (buffer_.empty() || buffer_.front().Empty()) { + read_all_chunks_ = true; + return ExceptionOr{ByteArray{}}; + } + + ByteArray first_chunk{buffer_.front()}; + buffer_.pop_front(); + + // If first_chunk is small enough to not overshoot the requested 'size', just + // return that. + if (first_chunk.size() <= size) { + return ExceptionOr{first_chunk}; + } else { + // Break first_chunk into 2 parts -- the first one of which (next_chunk) + // will be 'size' bytes long, and will be returned, and the second one of + // which (overflow_chunk) will be re-inserted into buffer_, at the head of + // the queue, to be served up in the next call to read(). + ByteArray next_chunk(first_chunk.data(), size); + buffer_.push_front( + ByteArray(first_chunk.data() + size, first_chunk.size() - size)); + return ExceptionOr{next_chunk}; + } } -#pragma pop_macro("CreateMutex") +Exception Pipe::Write(const ByteArray& data) { + BaseMutexLock lock(mutex_.get()); + return WriteLocked(data); +} + +void Pipe::MarkInputStreamClosed() { + BaseMutexLock lock(mutex_.get()); + if (input_stream_closed_) return; + input_stream_closed_ = true; + // Trigger cond_ to unblock a potentially-blocked call to read(), and to let + // it know to return Exception::IO. + cond_->Notify(); +} + +void Pipe::MarkOutputStreamClosed() { + BaseMutexLock lock(mutex_.get()); + if (output_stream_closed_) return; + // Write a sentinel null chunk before marking output_stream_closed as true. + WriteLocked(ByteArray{}); + output_stream_closed_ = true; +} + +Exception Pipe::WriteLocked(const ByteArray& data) { + if (input_stream_closed_ || output_stream_closed_) { + return {Exception::kIo}; + } + + buffer_.push_back(data); + // Trigger cond_ to unblock a potentially-blocked call to read(), now that + // there's more data for it to consume. + cond_->Notify(); + return {Exception::kSuccess}; +} + +} // namespace + +std::pair, std::unique_ptr> +CreatePipe() { + auto pipe = std::make_shared(); + return std::make_pair(std::make_unique(pipe), + std::make_unique(pipe)); +} } // namespace nearby diff --git a/internal/platform/pipe.h b/internal/platform/pipe.h index a96bfbbc..4d0af630 100644 --- a/internal/platform/pipe.h +++ b/internal/platform/pipe.h @@ -15,19 +15,23 @@ #ifndef PLATFORM_PUBLIC_PIPE_H_ #define PLATFORM_PUBLIC_PIPE_H_ -#include "internal/platform/base_pipe.h" +#include +#include + +#include "internal/platform/input_stream.h" +#include "internal/platform/output_stream.h" namespace nearby { -// See for details: -// http://google3/platform/base/base_pipe.h -class Pipe final : public BasePipe { - public: - Pipe(); - ~Pipe() override = default; - Pipe(Pipe&&) = delete; - Pipe& operator=(Pipe&&) = delete; -}; +// Creates a pipe for streaming data between threads. +// ``` +// auto [input, output] = CreatePipe(); +// ReaderThread(std::move(input)); +// WriterThread(std::move(output)); +// ``` +// Pipe stays valid as long as either `input` or `output` exist. +std::pair, std::unique_ptr> +CreatePipe(); } // namespace nearby diff --git a/internal/platform/pipe_test.cc b/internal/platform/pipe_test.cc index 08312836..23ee4da0 100644 --- a/internal/platform/pipe_test.cc +++ b/internal/platform/pipe_test.cc @@ -17,141 +17,137 @@ #include #include +#include #include +#include #include #include +#include #include "gtest/gtest.h" +#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/output_stream.h" #include "internal/platform/prng.h" #include "internal/platform/runnable.h" namespace nearby { +namespace { +constexpr size_t kChunkSize = 64 * 1024; +} + TEST(PipeTest, ConstructorDestructorWorks) { - Pipe pipe; + auto [input_stream, output_stream] = CreatePipe(); SUCCEED(); } TEST(PipeTest, SimpleWriteRead) { - Pipe pipe; - InputStream& input_stream{pipe.GetInputStream()}; - OutputStream& output_stream{pipe.GetOutputStream()}; - + auto [input_stream, output_stream] = CreatePipe(); std::string data("ABCD"); - EXPECT_TRUE(output_stream.Write(ByteArray(data)).Ok()); + EXPECT_TRUE(output_stream->Write(ByteArray(data)).Ok()); - ExceptionOr read_data = input_stream.Read(Pipe::kChunkSize); + ExceptionOr read_data = input_stream->Read(kChunkSize); EXPECT_TRUE(read_data.ok()); EXPECT_EQ(data, std::string(read_data.result())); } TEST(PipeTest, WriteEndClosedBeforeRead) { - Pipe pipe; - InputStream& input_stream{pipe.GetInputStream()}; - OutputStream& output_stream{pipe.GetOutputStream()}; + auto [input_stream, output_stream] = CreatePipe(); std::string data("ABCD"); - EXPECT_TRUE(output_stream.Write(ByteArray(data)).Ok()); + EXPECT_TRUE(output_stream->Write(ByteArray(data)).Ok()); // Close the write end before the read end has even begun reading. - EXPECT_TRUE(output_stream.Close().Ok()); + EXPECT_TRUE(output_stream->Close().Ok()); // We should still be able to read what was written. - ExceptionOr read_data = input_stream.Read(Pipe::kChunkSize); + ExceptionOr read_data = input_stream->Read(kChunkSize); EXPECT_TRUE(read_data.ok()); EXPECT_EQ(data, std::string(read_data.result())); // And after that, we should get our indication that all the data that could // ever be read, has already been read. - read_data = input_stream.Read(Pipe::kChunkSize); + read_data = input_stream->Read(kChunkSize); EXPECT_TRUE(read_data.ok()); EXPECT_TRUE(read_data.result().Empty()); } TEST(PipeTest, ReadEndClosedBeforeWrite) { - Pipe pipe; - InputStream& input_stream{pipe.GetInputStream()}; - OutputStream& output_stream{pipe.GetOutputStream()}; + auto [input_stream, output_stream] = CreatePipe(); // Close the read end before the write end has even begun writing. - EXPECT_TRUE(input_stream.Close().Ok()); + EXPECT_TRUE(input_stream->Close().Ok()); std::string data("ABCD"); - EXPECT_TRUE(output_stream.Write(ByteArray(data)).Raised(Exception::kIo)); + EXPECT_TRUE(output_stream->Write(ByteArray(data)).Raised(Exception::kIo)); } TEST(PipeTest, SizedReadMoreThanFirstChunkSize) { - Pipe pipe; - InputStream& input_stream{pipe.GetInputStream()}; - OutputStream& output_stream{pipe.GetOutputStream()}; + auto [input_stream, output_stream] = CreatePipe(); std::string data("ABCD"); - EXPECT_TRUE(output_stream.Write(ByteArray(data)).Ok()); + EXPECT_TRUE(output_stream->Write(ByteArray(data)).Ok()); // Even though we ask for double of what's there in the first chunk, we should // get back only what's there in that first chunk, and that's alright. - ExceptionOr read_data = input_stream.Read(data.size() * 2); + ExceptionOr read_data = input_stream->Read(data.size() * 2); EXPECT_TRUE(read_data.ok()); EXPECT_EQ(data, std::string(read_data.result())); } TEST(PipeTest, SizedReadLessThanFirstChunkSize) { - Pipe pipe; - InputStream& input_stream{pipe.GetInputStream()}; - OutputStream& output_stream{pipe.GetOutputStream()}; - + auto [input_stream, output_stream] = CreatePipe(); std::string data_first_part("ABCD"); std::string data_second_part("EFGHIJ"); std::string data = data_first_part + data_second_part; - EXPECT_TRUE(output_stream.Write(ByteArray(data)).Ok()); + EXPECT_TRUE(output_stream->Write(ByteArray(data)).Ok()); // When we ask for less than what's there in the first chunk, we should get // back exactly what we asked for, with the remainder still being available // for the next read. std::int64_t desired_size = data_first_part.size(); - ExceptionOr first_read_data = input_stream.Read(desired_size); + ExceptionOr first_read_data = input_stream->Read(desired_size); EXPECT_TRUE(first_read_data.ok()); EXPECT_EQ(data_first_part, std::string(first_read_data.result())); // Now read the remainder, and get everything that ought to have been left. - ExceptionOr second_read_data = input_stream.Read(Pipe::kChunkSize); + ExceptionOr second_read_data = input_stream->Read(kChunkSize); EXPECT_TRUE(second_read_data.ok()); EXPECT_EQ(data_second_part, std::string(second_read_data.result())); } TEST(PipeTest, ReadAfterInputStreamClosed) { - Pipe pipe; - InputStream& input_stream{pipe.GetInputStream()}; + auto [input_stream, output_stream] = CreatePipe(); - input_stream.Close(); + input_stream->Close(); - ExceptionOr read_data = input_stream.Read(Pipe::kChunkSize); + ExceptionOr read_data = input_stream->Read(kChunkSize); EXPECT_TRUE(read_data.ok()); EXPECT_TRUE(read_data.GetResult().Empty()); } TEST(PipeTest, WriteAfterOutputStreamClosed) { - Pipe pipe; - OutputStream& output_stream{pipe.GetOutputStream()}; + auto [input_stream, output_stream] = CreatePipe(); - output_stream.Close(); + output_stream->Close(); std::string data("ABCD"); - EXPECT_TRUE(output_stream.Write(ByteArray(data)).Raised(Exception::kIo)); + EXPECT_TRUE(output_stream->Write(ByteArray(data)).Raised(Exception::kIo)); } TEST(PipeTest, RepeatedClose) { - Pipe pipe; - InputStream& input_stream{pipe.GetInputStream()}; - OutputStream& output_stream{pipe.GetOutputStream()}; + auto [input_stream, output_stream] = CreatePipe(); - EXPECT_TRUE(output_stream.Close().Ok()); - EXPECT_TRUE(output_stream.Close().Ok()); - EXPECT_TRUE(output_stream.Close().Ok()); + EXPECT_TRUE(output_stream->Close().Ok()); + EXPECT_TRUE(output_stream->Close().Ok()); + EXPECT_TRUE(output_stream->Close().Ok()); - EXPECT_TRUE(input_stream.Close().Ok()); - EXPECT_TRUE(input_stream.Close().Ok()); - EXPECT_TRUE(input_stream.Close().Ok()); + EXPECT_TRUE(input_stream->Close().Ok()); + EXPECT_TRUE(input_stream->Close().Ok()); + EXPECT_TRUE(input_stream->Close().Ok()); } class Thread { @@ -186,17 +182,18 @@ TEST(PipeTest, ReadBlockedUntilWrite) { class ReaderRunnable { public: - ReaderRunnable(InputStream* input_stream, + ReaderRunnable(std::unique_ptr input_stream, absl::string_view expected_read_data, CrossThreadBool* ok_for_read_to_unblock) - : input_stream_(input_stream), + : input_stream_(std::move(input_stream)), expected_read_data_(expected_read_data), ok_for_read_to_unblock_(ok_for_read_to_unblock) {} + ReaderRunnable(ReaderRunnable&&) = default; ~ReaderRunnable() = default; // Signature "void()" satisfies Runnable. void operator()() { - ExceptionOr read_data = input_stream_->Read(Pipe::kChunkSize); + ExceptionOr read_data = input_stream_->Read(kChunkSize); // Make sure read() doesn't return before it's appropriate. if (!*ok_for_read_to_unblock_) { @@ -210,13 +207,12 @@ TEST(PipeTest, ReadBlockedUntilWrite) { } private: - InputStream* input_stream_; + std::unique_ptr input_stream_; const std::string expected_read_data_; CrossThreadBool* ok_for_read_to_unblock_; }; - Pipe pipe; - OutputStream& output_stream{pipe.GetOutputStream()}; + auto [input_stream, output_stream] = CreatePipe(); // State shared between this thread (the writer) and reader_thread. CrossThreadBool ok_for_read_to_unblock = false; @@ -225,7 +221,7 @@ TEST(PipeTest, ReadBlockedUntilWrite) { // Kick off reader_thread. Thread reader_thread; reader_thread.Start( - ReaderRunnable(&pipe.GetInputStream(), data, &ok_for_read_to_unblock)); + ReaderRunnable(std::move(input_stream), data, &ok_for_read_to_unblock)); // Introduce a delay before we actually write anything. absl::SleepFor(absl::Seconds(5)); @@ -236,7 +232,7 @@ TEST(PipeTest, ReadBlockedUntilWrite) { ok_for_read_to_unblock = true; // Perform the actual write. - EXPECT_TRUE(output_stream.Write(ByteArray(data)).Ok()); + EXPECT_TRUE(output_stream->Write(ByteArray(data)).Ok()); // And wait for reader_thread to finish. reader_thread.Join(); @@ -247,6 +243,7 @@ TEST(PipeTest, ConcurrentWriteAndRead) { protected: explicit BaseRunnable(const std::vector& chunks) : chunks_(chunks) {} + BaseRunnable(BaseRunnable&&) = default; virtual ~BaseRunnable() = default; void RandomSleep() { @@ -267,9 +264,10 @@ TEST(PipeTest, ConcurrentWriteAndRead) { class WriterRunnable : public BaseRunnable { public: - WriterRunnable(OutputStream* output_stream, + WriterRunnable(std::unique_ptr output_stream, const std::vector& chunks) - : BaseRunnable(chunks), output_stream_(output_stream) {} + : BaseRunnable(chunks), output_stream_(std::move(output_stream)) {} + WriterRunnable(WriterRunnable&&) = default; ~WriterRunnable() override = default; void operator()() { @@ -283,14 +281,15 @@ TEST(PipeTest, ConcurrentWriteAndRead) { } private: - OutputStream* output_stream_; + std::unique_ptr output_stream_; }; class ReaderRunnable : public BaseRunnable { public: - ReaderRunnable(InputStream* input_stream, + ReaderRunnable(std::unique_ptr input_stream, const std::vector& chunks) - : BaseRunnable(chunks), input_stream_(input_stream) {} + : BaseRunnable(chunks), input_stream_(std::move(input_stream)) {} + ReaderRunnable(ReaderRunnable&&) = default; ~ReaderRunnable() override = default; void operator()() { @@ -304,8 +303,7 @@ TEST(PipeTest, ConcurrentWriteAndRead) { std::string actual_data; while (true) { RandomSleep(); // Random pauses before each read. - ExceptionOr read_data = - input_stream_->Read(Pipe::kChunkSize); + ExceptionOr read_data = input_stream_->Read(kChunkSize); if (read_data.ok()) { ByteArray result = read_data.result(); if (result.Empty()) { @@ -322,11 +320,10 @@ TEST(PipeTest, ConcurrentWriteAndRead) { } private: - InputStream* input_stream_; + std::unique_ptr input_stream_; }; - Pipe pipe; - + auto [input_stream, output_stream] = CreatePipe(); std::vector chunks; chunks.push_back("ABCD"); chunks.push_back("EFGH"); @@ -334,8 +331,8 @@ TEST(PipeTest, ConcurrentWriteAndRead) { Thread writer_thread; Thread reader_thread; - writer_thread.Start(WriterRunnable(&pipe.GetOutputStream(), chunks)); - reader_thread.Start(ReaderRunnable(&pipe.GetInputStream(), chunks)); + writer_thread.Start(WriterRunnable(std::move(output_stream), chunks)); + reader_thread.Start(ReaderRunnable(std::move(input_stream), chunks)); writer_thread.Join(); reader_thread.Join(); } From 748bf9fb187c3daf0bb35a26fb47d70dfca1026a Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Sun, 13 Aug 2023 18:23:56 -0700 Subject: [PATCH 097/128] [fp-rs] Added unit tests to cover common portion of Rust Bluetooth library. --- fastpair/rust/bluetooth/src/common/address.rs | 84 +++++++++++++++++++ .../bluetooth/src/common/advertisement.rs | 57 ++++++++++++- fastpair/rust/bluetooth/src/common/error.rs | 2 +- 3 files changed, 141 insertions(+), 2 deletions(-) diff --git a/fastpair/rust/bluetooth/src/common/address.rs b/fastpair/rust/bluetooth/src/common/address.rs index db0481f6..bff124d7 100644 --- a/fastpair/rust/bluetooth/src/common/address.rs +++ b/fastpair/rust/bluetooth/src/common/address.rs @@ -93,3 +93,87 @@ impl From for u64 { u64::from_le_bytes(bytes) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn ble_address_new() { + let addr = BleAddress::new(0x112233445566, BleAddressKind::Public); + assert_eq!(addr.val, [0x66, 0x55, 0x44, 0x33, 0x22, 0x11]); + assert_eq!(addr.kind, BleAddressKind::Public); + } + + #[test] + fn ble_address_get_kind() { + let addr_public = + BleAddress::new(0x112233445566, BleAddressKind::Public); + assert_eq!(addr_public.get_kind(), BleAddressKind::Public); + + let addr_random = + BleAddress::new(0xAABBCCDDEEFF, BleAddressKind::Random); + assert_eq!(addr_random.get_kind(), BleAddressKind::Random); + } + + #[test] + fn ble_address_into_u64() { + let ble_addr = BleAddress::new(0x112233445566, BleAddressKind::Public); + let u64_addr: u64 = ble_addr.into(); + assert_eq!(u64_addr, 0x112233445566); + } + + #[test] + fn classic_address_from_u64() { + let u64_addr = 0x112233445566; + let classic_addr: ClassicAddress = u64_addr.into(); + assert_eq!(classic_addr.0, [0x66, 0x55, 0x44, 0x33, 0x22, 0x11]); + } + + #[test] + fn try_from_ble_address_to_classic() { + let ble_addr = BleAddress::new(0x112233445566, BleAddressKind::Public); + let result: Result = + TryFrom::try_from(ble_addr); + assert!(result.is_ok()); + assert_eq!(result.unwrap().0, [0x66, 0x55, 0x44, 0x33, 0x22, 0x11]); + + let ble_addr_random = + BleAddress::new(0xAABBCCDDEEFF, BleAddressKind::Random); + let result: Result = + TryFrom::try_from(ble_addr_random); + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + BluetoothError::BadTypeConversion(_), + )); + } + + #[test] + fn test_u64_to_6lsb() { + // Test a case where the input number is smaller than 6 bytes + let num = 0x123456; + let expected_result = [0x56, 0x34, 0x12, 0, 0, 0]; + let result = u64_to_6lsb(num); + assert_eq!(result, expected_result); + + // Test a case where the input number is exactly 6 bytes + let num = 0xAABBCCDDEEFF; + let expected_result = [0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA]; + let result = u64_to_6lsb(num); + assert_eq!(result, expected_result); + + // Test a case where the number is too large so the two most significant + // bytes get dropped. + let num = 0x1122334455667788; + let expected_result = [0x88, 0x77, 0x66, 0x55, 0x44, 0x33]; + let result = u64_to_6lsb(num); + assert_eq!(result, expected_result); + + // Test a case where the input number is 0 + let num = 0; + let expected_result = [0, 0, 0, 0, 0, 0]; + let result = u64_to_6lsb(num); + assert_eq!(result, expected_result); + } +} diff --git a/fastpair/rust/bluetooth/src/common/advertisement.rs b/fastpair/rust/bluetooth/src/common/advertisement.rs index 9fb7f999..93413079 100644 --- a/fastpair/rust/bluetooth/src/common/advertisement.rs +++ b/fastpair/rust/bluetooth/src/common/advertisement.rs @@ -96,7 +96,7 @@ pub enum BleDataTypeId { /// Struct representing the Bluetooth Service Data common data type. `U` should /// be one of the valid uuid sizes, specified in: /// Bluetooth Supplement to the Core Specification, Part A, Section 1.11. -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq, Debug)] pub struct ServiceData { uuid: U, data: Vec, @@ -115,3 +115,58 @@ impl ServiceData { &self.data } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::BleAddressKind; + + #[test] + fn ble_advertisement_new() { + let address = BleAddress::new(0x112233445566, BleAddressKind::Public); + let ad = BleAdvertisement::new(address, Some(-60), Some(10)); + assert_eq!(ad.address(), address); + assert_eq!(ad.rssi(), Some(-60)); + assert_eq!(ad.tx_power(), Some(10)); + assert!(ad.service_data_16bit_uuid.is_none()); + } + + #[test] + fn ble_advertisement_set_and_get_service_data() { + let address = BleAddress::new(0x112233445566, BleAddressKind::Public); + let mut ad = BleAdvertisement::new(address, Some(-60), Some(10)); + + let service_data = vec![ + ServiceData::new(0x1234, vec![0x01, 0x02, 0x03]), + ServiceData::new(0x5678, vec![0x04, 0x05]), + ]; + + ad.set_service_data_16bit_uuid(service_data.clone()); + + let retrieved_service_data = ad.service_data_16bit_uuid().unwrap(); + assert_eq!(*retrieved_service_data, service_data); + } + + #[test] + fn ble_advertisement_missing_service_data() { + let address = BleAddress::new(0x112233445566, BleAddressKind::Public); + let ad = BleAdvertisement::new(address, Some(-60), Some(10)); + + let result = ad.service_data_16bit_uuid(); + assert!(result.is_err()); + assert!(matches!( + result.unwrap_err(), + BluetoothError::FailedPrecondition(_) + )); + } + + #[test] + fn service_data_new() { + let uuid = 0x1234; + let data = vec![0x01, 0x02, 0x03]; + + let service_data = ServiceData::new(uuid, data.clone()); + assert_eq!(service_data.uuid(), uuid); + assert_eq!(*service_data.data(), data); + } +} diff --git a/fastpair/rust/bluetooth/src/common/error.rs b/fastpair/rust/bluetooth/src/common/error.rs index 8b20b36e..d0d43208 100644 --- a/fastpair/rust/bluetooth/src/common/error.rs +++ b/fastpair/rust/bluetooth/src/common/error.rs @@ -16,7 +16,7 @@ use thiserror::Error; /// Library error type. #[non_exhaustive] -#[derive(Error, Debug)] +#[derive(Error, Debug, PartialEq)] pub enum BluetoothError { /// Reported when the user attempts a bad type conversion, e.g. converting /// a BLE random address to a BT Classic address. From b1fa76f560100a228e5574d40e3d857c7c302a02 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Mon, 14 Aug 2023 16:29:02 -0700 Subject: [PATCH 098/128] [fp-rs] Wrote unit tests for Fast Pair Decoder in Rust. --- fastpair/rust/demo/rust/src/decoder.rs | 35 ++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/fastpair/rust/demo/rust/src/decoder.rs b/fastpair/rust/demo/rust/src/decoder.rs index dd0f80d4..4865a35b 100644 --- a/fastpair/rust/demo/rust/src/decoder.rs +++ b/fastpair/rust/demo/rust/src/decoder.rs @@ -47,3 +47,38 @@ impl FpDecoder { } } } + +mod tests { + use super::*; + + #[test] + fn test_get_model_id_valid() { + // Valid scenario: Length == 3 + let uuid: u16 = 0x1234; + let data = vec![0xAA, 0xBB, 0xCC]; + let service_data = ServiceData::new(uuid, data.clone()); + let result = FpDecoder::get_model_id_from_service_data(&service_data); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), data); + } + + #[test] + fn test_get_model_id_invalid() { + // Invalid scenario: Length < 3 + let uuid: u16 = 0x1234; + let data = vec![0xAA, 0xBB]; + let service_data = ServiceData::new(uuid, data); + let result = FpDecoder::get_model_id_from_service_data(&service_data); + assert!(result.is_err()); + } + + #[test] + fn test_get_model_id_unsupported() { + // Unsupported scenario: Length > 3 + let uuid: u16 = 0x1234; + let data = vec![0xAA, 0xBB, 0xCC, 0xDD]; + let service_data = ServiceData::new(uuid, data); + let result = FpDecoder::get_model_id_from_service_data(&service_data); + assert!(result.is_err()); + } +} From 169c04c43024d92d94b60f0bf79eab32d91cb080 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 15 Aug 2023 12:22:17 -0700 Subject: [PATCH 099/128] [fp-rs] Split fetcher.rs into directory module, added mock. --- fastpair/rust/demo/rust/src/advertisement.rs | 4 +- .../src/{fetcher.rs => fetcher/common.rs} | 63 +++++++------------ fastpair/rust/demo/rust/src/fetcher/fs.rs | 49 +++++++++++++++ fastpair/rust/demo/rust/src/fetcher/mock.rs | 43 +++++++++++++ fastpair/rust/demo/rust/src/fetcher/mod.rs | 22 +++++++ 5 files changed, 138 insertions(+), 43 deletions(-) rename fastpair/rust/demo/rust/src/{fetcher.rs => fetcher/common.rs} (59%) create mode 100644 fastpair/rust/demo/rust/src/fetcher/fs.rs create mode 100644 fastpair/rust/demo/rust/src/fetcher/mock.rs create mode 100644 fastpair/rust/demo/rust/src/fetcher/mod.rs diff --git a/fastpair/rust/demo/rust/src/advertisement.rs b/fastpair/rust/demo/rust/src/advertisement.rs index bb3dd601..58cc3338 100644 --- a/fastpair/rust/demo/rust/src/advertisement.rs +++ b/fastpair/rust/demo/rust/src/advertisement.rs @@ -16,7 +16,7 @@ use bluetooth::{BleAddress, BleAdvertisement, ServiceData}; use crate::{ decoder::FpDecoder, - fetcher::{FpFetcher, FpFetcherLocal}, + fetcher::{DeviceInfo, FpFetcher, FpFetcherFs}, }; /// Represents a FP device model ID. @@ -78,7 +78,7 @@ impl FpPairingAdvertisement { let model_id = format!("{}", u32::from_be_bytes(model_id.try_into().unwrap())); // Retrieve device info of the device corresponding to this model ID. - let fetcher = FpFetcherLocal::new(String::from("./local")); + let fetcher = FpFetcherFs::new(String::from("./local")); let device_info = fetcher .get_device_info_from_model_id(&model_id) .expect("Failed to create device info from model ID."); diff --git a/fastpair/rust/demo/rust/src/fetcher.rs b/fastpair/rust/demo/rust/src/fetcher/common.rs similarity index 59% rename from fastpair/rust/demo/rust/src/fetcher.rs rename to fastpair/rust/demo/rust/src/fetcher/common.rs index e94d49ad..ea7ed605 100644 --- a/fastpair/rust/demo/rust/src/fetcher.rs +++ b/fastpair/rust/demo/rust/src/fetcher/common.rs @@ -11,27 +11,11 @@ // 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::fs; use serde::Deserialize; use crate::advertisement::ModelId; -/// Holds Fast Pair device information parsed from JSON. -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub(crate) struct DeviceInfo { - image_url: String, - name: String, -} - -/// Holds top-level Fast Pair information parsed from JSON. See `local` -/// directory for format. -#[derive(Deserialize)] -struct JsonData { - device: DeviceInfo, -} - /// Types that can fetch Fast Pair data from external storage (e.g. filesystem, /// remote server). pub(crate) trait FpFetcher { @@ -41,36 +25,26 @@ pub(crate) trait FpFetcher { ) -> Result; } -/// A unit struct for retrieving Fast Pair information from the local filesystem. -pub(crate) struct FpFetcherLocal { - path: String, +/// Holds Fast Pair device information parsed from JSON. +#[derive(Clone, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct DeviceInfo { + image_url: String, + name: String, } -impl FpFetcherLocal { - pub(crate) fn new(path: String) -> Self { - FpFetcherLocal { path } - } -} - -impl FpFetcher for FpFetcherLocal { - /// Retrieve device information for the provided Model ID. Currently, - /// this information is saved locally. In the future, this should instead - /// be retrieved from a remote server and cached. - /// b/294456411 - fn get_device_info_from_model_id( - &self, - model_id: &ModelId, - ) -> Result { - let file_path = format!("{}/{}.json", self.path, model_id); - let contents = fs::read_to_string(file_path).expect("Couldn't find or open file."); - - let model_info: JsonData = serde_json::from_str(&contents)?; - - Ok(model_info.device) - } +/// Holds top-level Fast Pair information parsed from JSON. See `local` +/// directory for format. +#[derive(Deserialize)] +pub(super) struct JsonData { + device: DeviceInfo, } impl DeviceInfo { + pub(crate) fn new(image_url: String, name: String) -> Self { + DeviceInfo { image_url, name } + } + pub(crate) fn name(&self) -> &String { &self.name } @@ -79,3 +53,10 @@ impl DeviceInfo { &self.image_url } } + +impl JsonData { + // Returns the `DeviceInfo` associated with parsed self, consuming self. + pub(super) fn device(self) -> DeviceInfo { + self.device + } +} diff --git a/fastpair/rust/demo/rust/src/fetcher/fs.rs b/fastpair/rust/demo/rust/src/fetcher/fs.rs new file mode 100644 index 00000000..ee45955e --- /dev/null +++ b/fastpair/rust/demo/rust/src/fetcher/fs.rs @@ -0,0 +1,49 @@ +// 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. + +use std::fs; + +use crate::{ + advertisement::ModelId, + fetcher::{DeviceInfo, FpFetcher, JsonData}, +}; + +/// A struct for retrieving Fast Pair information from the local filesystem. +pub(crate) struct FpFetcherFs { + path: String, +} + +impl FpFetcherFs { + pub(crate) fn new(path: String) -> Self { + FpFetcherFs { path } + } +} + +impl FpFetcher for FpFetcherFs { + /// Retrieve device information for the provided Model ID. Currently, + /// this information is saved locally. In the future, this should instead + /// be retrieved from a remote server and cached. + /// b/294456411 + fn get_device_info_from_model_id( + &self, + model_id: &ModelId, + ) -> Result { + let file_path = format!("{}/{}.json", self.path, model_id); + let contents = fs::read_to_string(file_path).expect("Couldn't find or open file."); + + let model_info: JsonData = serde_json::from_str(&contents)?; + + Ok(model_info.device()) + } +} diff --git a/fastpair/rust/demo/rust/src/fetcher/mock.rs b/fastpair/rust/demo/rust/src/fetcher/mock.rs new file mode 100644 index 00000000..93f9351f --- /dev/null +++ b/fastpair/rust/demo/rust/src/fetcher/mock.rs @@ -0,0 +1,43 @@ +// 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. + +use crate::{ + advertisement::ModelId, + fetcher::{DeviceInfo, FpFetcher}, +}; + +/// A struct for mocking retrieval of Fast Pair data. +pub(crate) struct FpFetcherMock { + get_device_info_from_model_id: Result, +} + +impl FpFetcherMock { + pub(crate) fn new(get_device_info_from_model_id: Result) -> Self { + FpFetcherMock { + get_device_info_from_model_id, + } + } +} + +impl FpFetcher for FpFetcherMock { + fn get_device_info_from_model_id( + &self, + _model_id: &ModelId, + ) -> Result { + match &self.get_device_info_from_model_id { + Ok(result) => Ok(result.clone()), + Err(_) => Err(anyhow::anyhow!("intentional mock error")), + } + } +} diff --git a/fastpair/rust/demo/rust/src/fetcher/mod.rs b/fastpair/rust/demo/rust/src/fetcher/mod.rs new file mode 100644 index 00000000..98772f6e --- /dev/null +++ b/fastpair/rust/demo/rust/src/fetcher/mod.rs @@ -0,0 +1,22 @@ +// 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. + +pub(crate) mod common; +pub(crate) mod fs; + +#[cfg(test)] +pub(crate) mod mock; + +pub(crate) use common::*; +pub(crate) use fs::*; From fb95e26bf59dd39381d21194d228035f383755cd Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 15 Aug 2023 13:31:56 -0700 Subject: [PATCH 100/128] [fp-rs] Added FP Advertisement unit tests in Rust. --- .../bluetooth/src/common/advertisement.rs | 2 +- fastpair/rust/bluetooth/src/lib.rs | 4 +- fastpair/rust/demo/rust/src/advertisement.rs | 125 ++++++++++++++++-- fastpair/rust/demo/rust/src/api.rs | 12 +- 4 files changed, 125 insertions(+), 18 deletions(-) diff --git a/fastpair/rust/bluetooth/src/common/advertisement.rs b/fastpair/rust/bluetooth/src/common/advertisement.rs index 93413079..6eb679f3 100644 --- a/fastpair/rust/bluetooth/src/common/advertisement.rs +++ b/fastpair/rust/bluetooth/src/common/advertisement.rs @@ -33,7 +33,7 @@ type DecibelMilliwatts = i16; impl BleAdvertisement { /// Construct a new `BleAdvertisement` instance. - pub(crate) fn new( + pub fn new( address: BleAddress, rssi: Option, tx_power: Option, diff --git a/fastpair/rust/bluetooth/src/lib.rs b/fastpair/rust/bluetooth/src/lib.rs index ebe8384a..99da6362 100644 --- a/fastpair/rust/bluetooth/src/lib.rs +++ b/fastpair/rust/bluetooth/src/lib.rs @@ -17,8 +17,8 @@ mod common; use api::{BleAdapter, BleDevice, ClassicDevice}; pub use common::{ - BleAddress, BleAdvertisement, BleDataTypeId, BluetoothError, - ClassicAddress, PairingResult, ServiceData, + BleAddress, BleAddressKind, BleAdvertisement, BleDataTypeId, + BluetoothError, ClassicAddress, PairingResult, ServiceData, }; cfg_if::cfg_if! { diff --git a/fastpair/rust/demo/rust/src/advertisement.rs b/fastpair/rust/demo/rust/src/advertisement.rs index 58cc3338..9179ad75 100644 --- a/fastpair/rust/demo/rust/src/advertisement.rs +++ b/fastpair/rust/demo/rust/src/advertisement.rs @@ -14,10 +14,7 @@ use bluetooth::{BleAddress, BleAdvertisement, ServiceData}; -use crate::{ - decoder::FpDecoder, - fetcher::{DeviceInfo, FpFetcher, FpFetcherFs}, -}; +use crate::{decoder::FpDecoder, fetcher::FpFetcher}; /// Represents a FP device model ID. pub(crate) type ModelId = String; @@ -30,7 +27,7 @@ pub(crate) struct FpPairingAdvertisement { /// Estimated distance in meters of device from BLE adapter. distance: f64, model_id: ModelId, - name: String, + device_name: String, image_url: String, } @@ -39,6 +36,7 @@ impl FpPairingAdvertisement { pub(crate) fn new( adv: BleAdvertisement, service_data: &ServiceData, + fetcher: &Box, ) -> Result { let rssi = adv.rssi().ok_or(anyhow::anyhow!( "Windows advertisements should contain RSSI information." @@ -78,16 +76,13 @@ impl FpPairingAdvertisement { let model_id = format!("{}", u32::from_be_bytes(model_id.try_into().unwrap())); // Retrieve device info of the device corresponding to this model ID. - let fetcher = FpFetcherFs::new(String::from("./local")); - let device_info = fetcher - .get_device_info_from_model_id(&model_id) - .expect("Failed to create device info from model ID."); + let device_info = fetcher.get_device_info_from_model_id(&model_id)?; Ok(FpPairingAdvertisement { inner: adv, distance, model_id, - name: device_info.name().to_string(), + device_name: device_info.name().to_string(), image_url: device_info.image_url().to_string(), }) } @@ -109,8 +104,8 @@ impl FpPairingAdvertisement { &self.model_id } - pub(crate) fn name(&self) -> &String { - &self.name + pub(crate) fn device_name(&self) -> &String { + &self.device_name } pub(crate) fn image_url(&self) -> &String { @@ -149,3 +144,109 @@ pub(crate) fn distance_from_rssi_and_tx_power(rssi: i16, tx_power: i16) -> f64 { (f64::from(tx_power - rssi - RSSI_DROPOFF_AT_1_M)) / f64::from(10 * PATH_LOSS_EXPONENT), ) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::fetcher::{mock::FpFetcherMock, DeviceInfo}; + + use bluetooth::BleAddressKind; + + #[test] + fn test_new_fp_pairing_advertisement() { + let addr = BleAddress::new(0x112233, BleAddressKind::Public); + let ble_adv = BleAdvertisement::new(addr, Some(-60), Some(10)); + + let raw_data = vec![3, 2, 1]; + let expected_model_id = "197121"; // (3 << 16) + (2 << 8) + 1. + let service_data = ServiceData::new(0x123 as u16, raw_data); + + let image_url = String::from("image_url"); + let device_name = String::from("name"); + let device_info = Ok(DeviceInfo::new(image_url.clone(), device_name.clone())); + let fetcher: Box = Box::new(FpFetcherMock::new(device_info)); + + let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher); + + assert!(fp_adv.is_ok()); + let fp_adv = fp_adv.unwrap(); + assert_eq!(fp_adv.address(), addr); + assert_eq!(fp_adv.image_url(), &image_url); + assert_eq!(fp_adv.device_name(), &device_name); + assert_eq!(fp_adv.model_id(), &expected_model_id); + } + + #[test] + fn test_new_fp_pairing_advertisement_bad_rssi() { + let addr = BleAddress::new(0x112233, BleAddressKind::Public); + let ble_adv = BleAdvertisement::new(addr, None, Some(10)); + + let raw_data = vec![3, 2, 1]; + let service_data = ServiceData::new(0x123 as u16, raw_data); + + let device_info = Ok(DeviceInfo::new( + String::from("image_url"), + String::from("name"), + )); + let fetcher: Box = Box::new(FpFetcherMock::new(device_info)); + + let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher); + + assert!(fp_adv.is_err()); + } + + #[test] + fn test_new_fp_pairing_advertisement_bad_tx_power() { + let addr = BleAddress::new(0x112233, BleAddressKind::Public); + let ble_adv = BleAdvertisement::new(addr, Some(-60), None); + + let raw_data = vec![3, 2, 1]; + let service_data = ServiceData::new(0x123 as u16, raw_data); + + let device_info = Ok(DeviceInfo::new( + String::from("image_url"), + String::from("name"), + )); + let fetcher: Box = Box::new(FpFetcherMock::new(device_info)); + + let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher); + + assert!(fp_adv.is_err()); + } + + #[test] + fn test_new_fp_pairing_advertisement_bad_service_data() { + let addr = BleAddress::new(0x112233, BleAddressKind::Public); + let ble_adv = BleAdvertisement::new(addr, Some(-60), Some(10)); + + let raw_data = vec![4, 3, 2, 1]; + let service_data = ServiceData::new(0x123 as u16, raw_data); + + let device_info = Ok(DeviceInfo::new( + String::from("image_url"), + String::from("name"), + )); + let fetcher: Box = Box::new(FpFetcherMock::new(device_info)); + + let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher); + + assert!(fp_adv.is_err()); + } + + #[test] + fn test_new_fp_pairing_advertisement_bad_fetcher() { + let addr = BleAddress::new(0x112233, BleAddressKind::Public); + let ble_adv = BleAdvertisement::new(addr, Some(-60), Some(10)); + + let raw_data = vec![3, 2, 1]; + let service_data = ServiceData::new(0x123 as u16, raw_data); + + let fetcher: Box = Box::new(FpFetcherMock::new(Err(anyhow::anyhow!( + "mock intentional error" + )))); + + let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher); + + assert!(fp_adv.is_err()); + } +} diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index e80ae950..bb1b3a2f 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -9,7 +9,10 @@ use futures::executor; use tracing::{info, warn}; use ttl_cache::TtlCache; -use crate::advertisement::{FpPairingAdvertisement, ModelId}; +use crate::{ + advertisement::{FpPairingAdvertisement, ModelId}, + fetcher::{FpFetcher, FpFetcherFs}, +}; // Sends a device name to Flutter via `StreamSink` FFI layer. static DEVICE_STREAM: RwLock>>> = RwLock::new(None); @@ -29,7 +32,7 @@ async fn update_best_device(best_adv: FpPairingAdvertisement) { match DEVICE_STREAM.read().unwrap().as_ref() { Some(stream) => { stream.add(Some([ - best_adv.name().to_string(), + best_adv.device_name().to_string(), best_adv.image_url().to_string(), ])); } @@ -47,6 +50,7 @@ async fn update_best_device(best_adv: FpPairingAdvertisement) { fn new_best_fp_advertisement( advertisement: BleAdvertisement, service_data: &ServiceData, + fetcher: &Box, latest_advertisement_map: &mut HashMap, ) -> Option { // Analyze service data sections. @@ -57,7 +61,7 @@ fn new_best_fp_advertisement( return None; } - let fp_adv = match FpPairingAdvertisement::new(advertisement, service_data) { + let fp_adv = match FpPairingAdvertisement::new(advertisement, service_data, fetcher) { Ok(fp_adv) => fp_adv, Err(err) => { // If error during construction (e.g. non-discoverable @@ -130,6 +134,7 @@ pub fn init() { let mut latest_advertisement_map = HashMap::new(); let datatype_selector = vec![BleDataTypeId::ServiceData16BitUuid]; + let fetcher: Box = Box::new(FpFetcherFs::new(String::from("./local"))); loop { // Retrieve the next received advertisement. @@ -142,6 +147,7 @@ pub fn init() { if let Some(best_adv) = new_best_fp_advertisement( advertisement.clone(), service_data, + &fetcher, &mut latest_advertisement_map, ) { update_best_device(best_adv).await; From ec139d1e8b564bc27924c90a6dd0efc4159900aa Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Tue, 15 Aug 2023 17:19:31 -0700 Subject: [PATCH 101/128] Replace std::function with AnyInvocable Also refactored the callbacks to make sure that we call either on_success or on_failure callback, and only once. PiperOrigin-RevId: 557299838 --- connections/implementation/client_proxy.cc | 3 +- connections/implementation/client_proxy.h | 4 +- .../implementation/encryption_runner.cc | 88 ++++++++++++------- .../implementation/encryption_runner.h | 31 ++++--- 4 files changed, 78 insertions(+), 48 deletions(-) diff --git a/connections/implementation/client_proxy.cc b/connections/implementation/client_proxy.cc index e275b7a4..c3a2e6fe 100644 --- a/connections/implementation/client_proxy.cc +++ b/connections/implementation/client_proxy.cc @@ -26,6 +26,7 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/functional/any_invocable.h" #include "absl/strings/escaping.h" #include "absl/strings/str_format.h" #include "connections/v3/bandwidth_info.h" @@ -554,7 +555,7 @@ bool ClientProxy::IsConnectedToEndpoint(const std::string& endpoint_id) const { } std::vector ClientProxy::GetMatchingEndpoints( - std::function pred) const { + absl::AnyInvocable pred) const { MutexLock lock(&mutex_); std::vector connected_endpoints; diff --git a/connections/implementation/client_proxy.h b/connections/implementation/client_proxy.h index b7c97ed2..f19190e4 100644 --- a/connections/implementation/client_proxy.h +++ b/connections/implementation/client_proxy.h @@ -16,13 +16,13 @@ #define CORE_INTERNAL_CLIENT_PROXY_H_ #include -#include #include #include #include #include #include +#include "absl/functional/any_invocable.h" #include "connections/advertising_options.h" #include "connections/discovery_options.h" #include "connections/implementation/analytics/analytics_recorder.h" @@ -338,7 +338,7 @@ class ClientProxy final { bool ConnectionStatusMatches(const std::string& endpoint_id, Connection::Status status) const; std::vector GetMatchingEndpoints( - std::function pred) const; + absl::AnyInvocable pred) const; std::string GenerateLocalEndpointId(); void ScheduleClearLocalHighVisModeCacheEndpointIdAlarm(); diff --git a/connections/implementation/encryption_runner.cc b/connections/implementation/encryption_runner.cc index b424b5ab..43146980 100644 --- a/connections/implementation/encryption_runner.cc +++ b/connections/implementation/encryption_runner.cc @@ -17,15 +17,19 @@ #include #include #include +#include +#include #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" #include "internal/platform/base64_utils.h" #include "internal/platform/byte_array.h" -#include "internal/platform/exception.h" #include "internal/platform/cancelable_alarm.h" +#include "internal/platform/exception.h" #include "internal/platform/logging.h" namespace nearby { @@ -49,7 +53,7 @@ std::string ToHumanReadableString(const ByteArray& token) { bool HandleEncryptionSuccess(const std::string& endpoint_id, std::unique_ptr ukey2, - const EncryptionRunner::ResultListener& listener) { + EncryptionRunner::ResultListener& listener) { std::unique_ptr verification_string = ukey2->GetVerificationString(kMaxUkey2VerificationStringLength); if (verification_string == nullptr) { @@ -58,9 +62,9 @@ bool HandleEncryptionSuccess(const std::string& endpoint_id, ByteArray raw_authentication_token(*verification_string); - listener.on_success_cb(endpoint_id, std::move(ukey2), - ToHumanReadableString(raw_authentication_token), - raw_authentication_token); + listener.CallSuccessCallback(endpoint_id, std::move(ukey2), + ToHumanReadableString(raw_authentication_token), + raw_authentication_token); return true; } @@ -79,14 +83,14 @@ class ServerRunnable final { public: ServerRunnable(ClientProxy* client, ScheduledExecutor* alarm_executor, const std::string& endpoint_id, EndpointChannel* channel, - EncryptionRunner::ResultListener&& listener) + EncryptionRunner::ResultListener listener) : client_(client), alarm_executor_(alarm_executor), endpoint_id_(endpoint_id), channel_(channel), listener_(std::move(listener)) {} - void operator()() const { + void operator()() { CancelableAlarm timeout_alarm( "EncryptionRunner.StartServer() timeout", [this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); }, @@ -189,9 +193,9 @@ class ServerRunnable final { << endpoint_id_ << ")."; } - void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) const { + void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) { timeout_alarm->Cancel(); - listener_.on_failure_cb(endpoint_id_, channel_); + listener_.CallFailureCallback(endpoint_id_, channel_); } void HandleAlertException( @@ -217,14 +221,14 @@ class ClientRunnable final { public: ClientRunnable(ClientProxy* client, ScheduledExecutor* alarm_executor, const std::string& endpoint_id, EndpointChannel* channel, - EncryptionRunner::ResultListener&& listener) + EncryptionRunner::ResultListener listener) : client_(client), alarm_executor_(alarm_executor), endpoint_id_(endpoint_id), channel_(channel), listener_(std::move(listener)) {} - void operator()() const { + void operator()() { CancelableAlarm timeout_alarm( "EncryptionRunner.StartClient() timeout", [this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); }, @@ -326,9 +330,9 @@ class ClientRunnable final { << endpoint_id_ << ")."; } - void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) const { + void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) { timeout_alarm->Cancel(); - listener_.on_failure_cb(endpoint_id_, channel_); + listener_.CallFailureCallback(endpoint_id_, channel_); } void HandleAlertException( @@ -359,28 +363,46 @@ EncryptionRunner::~EncryptionRunner() { alarm_executor_.Shutdown(); } -void EncryptionRunner::StartServer( - ClientProxy* client, const std::string& endpoint_id, - EndpointChannel* endpoint_channel, - EncryptionRunner::ResultListener&& listener) { - server_executor_.Execute( - "encryption-server", - [runnable{ServerRunnable(client, &alarm_executor_, endpoint_id, - endpoint_channel, std::move(listener))}]() { - runnable(); - }); +void EncryptionRunner::StartServer(ClientProxy* client, + const std::string& endpoint_id, + EndpointChannel* 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) { - client_executor_.Execute( - "encryption-client", - [runnable{ClientRunnable(client, &alarm_executor_, endpoint_id, - endpoint_channel, std::move(listener))}]() { - runnable(); - }); +void EncryptionRunner::StartClient(ClientProxy* client, + const std::string& endpoint_id, + EndpointChannel* 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)); +} + +void EncryptionRunner::ResultListener::CallSuccessCallback( + const std::string& endpoint_id, + std::unique_ptr ukey2, + const std::string& auth_token, const ByteArray& raw_auth_token) { + if (on_success_cb) { + std::move(on_success_cb)(endpoint_id, std::move(ukey2), auth_token, + raw_auth_token); + } + Reset(); +} + +void EncryptionRunner::ResultListener::CallFailureCallback( + const std::string& endpoint_id, EndpointChannel* channel) { + if (on_failure_cb) { + std::move(on_failure_cb)(endpoint_id, channel); + } + Reset(); +} + +void EncryptionRunner::ResultListener::Reset() { + on_success_cb = nullptr; + on_failure_cb = nullptr; } } // namespace connections diff --git a/connections/implementation/encryption_runner.h b/connections/implementation/encryption_runner.h index a8bb367d..7c2b401f 100644 --- a/connections/implementation/encryption_runner.h +++ b/connections/implementation/encryption_runner.h @@ -18,6 +18,7 @@ #include #include "securegcm/ukey2_handshake.h" +#include "absl/functional/any_invocable.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" #include "connections/listeners.h" @@ -39,14 +40,20 @@ class EncryptionRunner { ~EncryptionRunner(); struct ResultListener { + void CallSuccessCallback(const std::string& endpoint_id, + std::unique_ptr ukey2, + const std::string& auth_token, + const ByteArray& raw_auth_token); + void CallFailureCallback(const std::string& endpoint_id, + EndpointChannel* channel); + void Reset(); + // @EncryptionRunnerThread - std::function ukey2, - const std::string& auth_token, - const ByteArray& raw_auth_token)> - on_success_cb = [](const std::string&, - std::unique_ptr, - const std::string&, const ByteArray&) {}; + absl::AnyInvocable ukey2, + const std::string& auth_token, + 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. @@ -57,19 +64,19 @@ class EncryptionRunner { // channel to the same endpoint. // // @EncryptionRunnerThread - std::function - on_failure_cb = [](const std::string&, EndpointChannel*) {}; + absl::AnyInvocable + on_failure_cb; }; // @AnyThread void StartServer(ClientProxy* client, const std::string& endpoint_id, EndpointChannel* endpoint_channel, - ResultListener&& result_listener); + ResultListener result_listener); // @AnyThread void StartClient(ClientProxy* client, const std::string& endpoint_id, EndpointChannel* endpoint_channel, - ResultListener&& result_listener); + ResultListener result_listener); private: ScheduledExecutor alarm_executor_; From 73635815ab31af199afe8732d0318062d51d174d Mon Sep 17 00:00:00 2001 From: hai007 Date: Wed, 16 Aug 2023 09:03:13 -0700 Subject: [PATCH 102/128] [Analytics] Filter unparsed endpoint id. PiperOrigin-RevId: 557503948 --- proto/sharing_enums.proto | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index 13a0fd00..eac4b63e 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -572,6 +572,15 @@ enum AdvertisingMode { FOREGROUND_ADVERTISING_MODE = 4; } +// The Nearby Sharing discovery mode. +enum DiscoveryMode { + UNKNOWN_DISCOVERY_MODE = 0; + SCREEN_OFF_DISCOVERY_MODE = 1; + BACKGROUND_DISCOVERY_MODE = 2; + MIDGROUND_DISCOVERY_MODE = 3; + FOREGROUND_DISCOVERY_MODE = 4; +} + // The class name of chimera activity. enum ActivityName { UNKNOWN_ACTIVITY = 0; From 55194622a7b7e9066f80f90675b06eb639612161 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Wed, 16 Aug 2023 18:11:43 -0700 Subject: [PATCH 103/128] Migrate to AnyInvocable PiperOrigin-RevId: 557660146 --- connections/implementation/BUILD | 1 + connections/implementation/endpoint_manager.cc | 10 +++++----- connections/implementation/endpoint_manager.h | 5 +++-- connections/implementation/offline_simulation_user.cc | 3 ++- connections/implementation/offline_simulation_user.h | 8 +++++--- connections/implementation/simulation_user.cc | 2 +- connections/implementation/simulation_user.h | 7 ++++--- 7 files changed, 21 insertions(+), 15 deletions(-) diff --git a/connections/implementation/BUILD b/connections/implementation/BUILD index fe357f98..6c06e435 100644 --- a/connections/implementation/BUILD +++ b/connections/implementation/BUILD @@ -186,6 +186,7 @@ cc_library( "//internal/platform:base", "//internal/platform:test_util", "//internal/platform:types", + "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/functional:bind_front", "@com_google_absl//absl/strings", "@com_google_googletest//:gtest_for_library_testonly", diff --git a/connections/implementation/endpoint_manager.cc b/connections/implementation/endpoint_manager.cc index 57f75942..4a9f5a32 100644 --- a/connections/implementation/endpoint_manager.cc +++ b/connections/implementation/endpoint_manager.cc @@ -97,7 +97,7 @@ class EndpointManager::LockedFrameProcessor { void EndpointManager::EndpointChannelLoopRunnable( const std::string& runnable_name, ClientProxy* client, const std::string& endpoint_id, - std::function(EndpointChannel*)> handler) { + absl::AnyInvocable(EndpointChannel*)> handler) { // EndpointChannelManager will not let multiple channels exist simultaneously // for the same endpoint_id; it will be closing "old" channels as new ones // come. @@ -786,11 +786,11 @@ void EndpointManager::EndpointState::StartEndpointReader(Runnable&& runnable) { } void EndpointManager::EndpointState::StartEndpointKeepAliveManager( - std::function runnable) { + absl::AnyInvocable runnable) { keep_alive_thread_.Execute( - "keep-alive", - [runnable, keep_alive_waiter_mutex = keep_alive_waiter_mutex_.get(), - keep_alive_waiter = keep_alive_waiter_.get()]() { + "keep-alive", [runnable = std::move(runnable), + keep_alive_waiter_mutex = keep_alive_waiter_mutex_.get(), + keep_alive_waiter = keep_alive_waiter_.get()]() mutable { runnable(keep_alive_waiter_mutex, keep_alive_waiter); }); } diff --git a/connections/implementation/endpoint_manager.h b/connections/implementation/endpoint_manager.h index c60e1484..5bfd0fbf 100644 --- a/connections/implementation/endpoint_manager.h +++ b/connections/implementation/endpoint_manager.h @@ -25,6 +25,7 @@ #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/implementation/client_proxy.h" @@ -189,7 +190,7 @@ class EndpointManager { void StartEndpointReader(Runnable&& runnable); void StartEndpointKeepAliveManager( - std::function runnable); + absl::AnyInvocable runnable); private: const std::string endpoint_id_; @@ -245,7 +246,7 @@ class EndpointManager { void EndpointChannelLoopRunnable( const std::string& runnable_name, ClientProxy* client_proxy, const std::string& endpoint_id, - std::function(EndpointChannel*)> handler); + absl::AnyInvocable(EndpointChannel*)> handler); static void WaitForLatch(const std::string& method_name, CountDownLatch* latch); diff --git a/connections/implementation/offline_simulation_user.cc b/connections/implementation/offline_simulation_user.cc index 03cfaa80..ae31da9f 100644 --- a/connections/implementation/offline_simulation_user.cc +++ b/connections/implementation/offline_simulation_user.cc @@ -14,6 +14,7 @@ #include "connections/implementation/offline_simulation_user.h" +#include "absl/functional/any_invocable.h" #include "absl/functional/bind_front.h" #include "connections/listeners.h" #include "internal/platform/byte_array.h" @@ -86,7 +87,7 @@ void OfflineSimulationUser::OnPayloadProgress(absl::string_view endpoint_id, } bool OfflineSimulationUser::WaitForProgress( - std::function predicate, + absl::AnyInvocable predicate, absl::Duration timeout) { Future future; { diff --git a/connections/implementation/offline_simulation_user.h b/connections/implementation/offline_simulation_user.h index af92d05b..12e46d60 100644 --- a/connections/implementation/offline_simulation_user.h +++ b/connections/implementation/offline_simulation_user.h @@ -18,6 +18,7 @@ #include #include "gtest/gtest.h" +#include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/offline_service_controller.h" @@ -131,8 +132,9 @@ class OfflineSimulationUser { const DiscoveredInfo& GetDiscovered() const { return discovered_; } ByteArray GetInfo() const { return info_; } - bool WaitForProgress(std::function pred, - absl::Duration timeout); + bool WaitForProgress( + absl::AnyInvocable pred, + absl::Duration timeout); Payload& GetPayload() { return payload_; } void SendPayload(Payload payload) { @@ -203,7 +205,7 @@ class OfflineSimulationUser { CountDownLatch* payload_latch_ = nullptr; CountDownLatch* disconnect_latch_ = nullptr; Future* future_ = nullptr; - std::function predicate_; + absl::AnyInvocable predicate_; ClientProxy client_; OfflineServiceController ctrl_; }; diff --git a/connections/implementation/simulation_user.cc b/connections/implementation/simulation_user.cc index ea8798b2..0c6dd570 100644 --- a/connections/implementation/simulation_user.cc +++ b/connections/implementation/simulation_user.cc @@ -76,7 +76,7 @@ void SimulationUser::OnPayloadProgress(absl::string_view endpoint_id, } bool SimulationUser::WaitForProgress( - std::function predicate, + absl::AnyInvocable predicate, absl::Duration timeout) { Future future; { diff --git a/connections/implementation/simulation_user.h b/connections/implementation/simulation_user.h index 487fca46..ef68db3d 100644 --- a/connections/implementation/simulation_user.h +++ b/connections/implementation/simulation_user.h @@ -134,8 +134,9 @@ class SimulationUser { const DiscoveredInfo& GetDiscovered() const { return discovered_; } ByteArray GetInfo() const { return info_; } - bool WaitForProgress(std::function pred, - absl::Duration timeout); + bool WaitForProgress( + absl::AnyInvocable pred, + absl::Duration timeout); protected: // ConnectionListener callbacks @@ -169,7 +170,7 @@ class SimulationUser { CountDownLatch* lost_latch_ = nullptr; CountDownLatch* payload_latch_ = nullptr; Future* future_ = nullptr; - std::function predicate_; + absl::AnyInvocable predicate_; ByteArray info_; Mediums mediums_; AdvertisingOptions advertising_options_; From 78dd1bab2ce72e22c941c4ef4ba110854b1a5b07 Mon Sep 17 00:00:00 2001 From: hai007 Date: Wed, 16 Aug 2023 18:44:52 -0700 Subject: [PATCH 104/128] [Nearby Sharing] 1. log as CONNECTION_STATUS_CANCELLATION status code if sender cancels receiver after connection setup. 2. add a new status(TIMED_OUT_READ_FRAME) when read frame time out. PiperOrigin-RevId: 557666045 --- proto/sharing_enums.proto | 47 ++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index eac4b63e..bd99d73b 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -283,6 +283,11 @@ enum EstablishConnectionStatus { CONNECTION_STATUS_SUCCESS = 1; CONNECTION_STATUS_FAILURE = 2; CONNECTION_STATUS_CANCELLATION = 3; + CONNECTION_STATUS_MEDIA_UNAVAILABLE_ATTACHMENT = 4; + CONNECTION_STATUS_FAILED_PAIRED_KEYHANDSHAKE = 5; + CONNECTION_STATUS_FAILED_WRITE_INTRODUCTION = 6; + CONNECTION_STATUS_FAILED_NULL_CONNECTION = 7; + CONNECTION_STATUS_FAILED_NO_TRANSFER_UPDATE_CALLBACK = 8; } // The status of sending and receiving attachments. Used by SEND_ATTACHMENTS. @@ -293,22 +298,22 @@ enum AttachmentTransmissionStatus { CANCELED_ATTACHMENT_TRANSMISSION_STATUS = 2; FAILED_ATTACHMENT_TRANSMISSION_STATUS = 3; - REJECTED_ATTACHMENT = 4; - TIMED_OUT_ATTACHMENT = 5; + REJECTED_ATTACHMENT = 4 [deprecated = true]; + TIMED_OUT_ATTACHMENT = 5 [deprecated = true]; AWAITING_REMOTE_ACCEPTANCE_FAILED_ATTACHMENT = 6 [deprecated = true]; - NOT_ENOUGH_SPACE_ATTACHMENT = 7; - FAILED_NO_TRANSFER_UPDATE_CALLBACK = 8; - MEDIA_UNAVAILABLE_ATTACHMENT = 9; - UNSUPPORTED_ATTACHMENT_TYPE_ATTACHMENT = 10; - NO_ATTACHMENT_FOUND = 11; - FAILED_NO_SHARE_TARGET_ENDPOINT = 12; - FAILED_PAIRED_KEYHANDSHAKE = 13; - FAILED_NULL_CONNECTION = 14; - FAILED_NO_PAYLOAD = 15; - FAILED_WRITE_INTRODUCTION = 16; + NOT_ENOUGH_SPACE_ATTACHMENT = 7 [deprecated = true]; + FAILED_NO_TRANSFER_UPDATE_CALLBACK = 8 [deprecated = true]; + MEDIA_UNAVAILABLE_ATTACHMENT = 9 [deprecated = true]; + UNSUPPORTED_ATTACHMENT_TYPE_ATTACHMENT = 10 [deprecated = true]; + NO_ATTACHMENT_FOUND = 11 [deprecated = true]; + FAILED_NO_SHARE_TARGET_ENDPOINT = 12 [deprecated = true]; + FAILED_PAIRED_KEYHANDSHAKE = 13 [deprecated = true]; + FAILED_NULL_CONNECTION = 14 [deprecated = true]; + FAILED_NO_PAYLOAD = 15 [deprecated = true]; + FAILED_WRITE_INTRODUCTION = 16 [deprecated = true]; // The remote response is either missing or has an unknown type. - FAILED_UNKNOWN_REMOTE_RESPONSE = 17; + FAILED_UNKNOWN_REMOTE_RESPONSE = 17 [deprecated = true]; // Breakdowns of FAILED_NULL_CONNECTION (Desktop side) FAILED_NULL_CONNECTION_INIT_OUTGOING = 18; @@ -317,9 +322,21 @@ enum AttachmentTransmissionStatus { // Breakdowns of FAILED_NULL_CONNECTION (android side) // Connection failed due to Wifi is disconnected or Bluetooth setting is off // or user turn on airplane mode. - FAILED_NULL_CONNECTION_LOST_CONNECTIVITY = 20; + FAILED_NULL_CONNECTION_LOST_CONNECTIVITY = 20 [deprecated = true]; // Unexpected connection failure. - FAILED_NULL_CONNECTION_FAILURE = 21; + FAILED_NULL_CONNECTION_FAILURE = 21 [deprecated = true]; + + REJECTED_ATTACHMENT_TRANSMISSION_STATUS = 22; + TIMED_OUT_ATTACHMENT_TRANSMISSION_STATUS = 23; + NOT_ENOUGH_SPACE_ATTACHMENT_TRANSMISSION_STATUS = 24; + UNSUPPORTED_ATTACHMENT_TYPE_ATTACHMENT_TRANSMISSION_STATUS = 25; + FAILED_UNKNOWN_REMOTE_RESPONSE_TRANSMISSION_STATUS = 26; + // Connection failed due to Wifi is disconnected or Bluetooth setting is off + // or user turn on airplane mode. + NO_RESPONSE_FRAME_CONNECTION_CLOSED_LOST_CONNECTIVITY_TRANSMISSION_STATUS = + 27; + // Unexpected connection failure due to no response frame. + NO_RESPONSE_FRAME_CONNECTION_CLOSED_TRANSMISSION_STATUS = 28; } // Generic result status of NearbyConnections API calls. From 4aa52b45f92e18762df7217bc1240c27dae91da9 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Thu, 17 Aug 2023 13:26:19 -0700 Subject: [PATCH 105/128] Add GATT client PiperOrigin-RevId: 557919250 --- .../apple/Mediums/BLEv2/GNCBLEError.h | 3 + .../Mediums/BLEv2/GNCBLEGATTCharacteristic.h | 27 +- .../Mediums/BLEv2/GNCBLEGATTCharacteristic.m | 13 +- .../apple/Mediums/BLEv2/GNCBLEGATTClient.h | 117 +++ .../apple/Mediums/BLEv2/GNCBLEGATTClient.m | 299 +++++++ .../apple/Mediums/BLEv2/GNCBLEGATTServer.h | 12 +- .../apple/Mediums/BLEv2/GNCBLEGATTServer.m | 2 +- .../apple/Mediums/BLEv2/GNCPeripheral.h | 167 ++++ .../apple/Mediums/BLEv2/GNCPeripheral.m | 34 + .../Mediums/BLEv2/GNCPeripheralManager.h | 4 +- .../Mediums/BLEv2/GNCPeripheralManager.m | 4 +- .../implementation/apple/Mediums/BUILD | 4 + .../platform/implementation/apple/Tests/BUILD | 13 + .../Tests/GNCBLEGATTCharacteristicTest.mm | 7 +- .../apple/Tests/GNCBLEGATTClient+Testing.h | 40 + .../apple/Tests/GNCBLEGATTClientTest.m | 748 ++++++++++++++++++ .../apple/Tests/GNCBLEGATTServerTest.m | 4 +- .../apple/Tests/GNCFakePeripheral.h | 60 ++ .../apple/Tests/GNCFakePeripheral.m | 118 +++ 19 files changed, 1647 insertions(+), 29 deletions(-) create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.m create mode 100644 internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h create mode 100644 internal/platform/implementation/apple/Tests/GNCBLEGATTClientTest.m create mode 100644 internal/platform/implementation/apple/Tests/GNCFakePeripheral.h create mode 100644 internal/platform/implementation/apple/Tests/GNCFakePeripheral.m diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h index 5eab7ec3..eb32fee7 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h @@ -23,4 +23,7 @@ typedef NS_ERROR_ENUM(GNCBLEErrorDomain, GNCBLEError){ GNCBLEErrorDuplicateCharacteristic, GNCBLEErrorInvalidServiceData, GNCBLEErrorAlreadyAdvertising, + GNCBLEErrorInvalidCharacteristic, + GNCBLEErrorAlreadyDiscoveringSpecifiedCharacteristics, + GNCBLEErrorAlreadyReadingCharacteristic, }; diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h index a0e56440..de21587b 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h @@ -15,20 +15,24 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + /** A container for GATT characteristic information. */ @interface GNCBLEGATTCharacteristic : NSObject /** @remark init is not an available initializer. */ -- (nonnull instancetype)init NS_UNAVAILABLE; +- (instancetype)init NS_UNAVAILABLE; /** - * Creates a container for GATT characteristic information. + * Creates a container for GATT characteristic information with properties. * * @param characteristicUUID The UUID of the characteristic. * @param serviceUUID The UUID of the service. + * @param properties The properties of the characteristic. */ -- (instancetype)initWithUUID:(nonnull CBUUID *)characteristicUUID - serviceUUID:(nonnull CBUUID *)serviceUUID; +- (instancetype)initWithUUID:(CBUUID *)characteristicUUID + serviceUUID:(CBUUID *)serviceUUID + properties:(CBCharacteristicProperties)properties; /** * Creates a container for GATT characteristic information with permissions and properties. @@ -38,17 +42,16 @@ * @param permissions The permissions of the characteristic value. * @param properties The properties of the characteristic. */ -- (nonnull instancetype)initWithUUID:(nonnull CBUUID *)characteristicUUID - serviceUUID:(nonnull CBUUID *)serviceUUID - permissions:(CBAttributePermissions)permissions - properties:(CBCharacteristicProperties)properties - NS_DESIGNATED_INITIALIZER; +- (instancetype)initWithUUID:(CBUUID *)characteristicUUID + serviceUUID:(CBUUID *)serviceUUID + permissions:(CBAttributePermissions)permissions + properties:(CBCharacteristicProperties)properties NS_DESIGNATED_INITIALIZER; /** The 128-bit UUID that identifies the characteristic. */ -@property(nonatomic, readonly, nonnull) CBUUID *characteristicUUID; +@property(nonatomic, readonly) CBUUID *characteristicUUID; /** The 128-bit UUID that identifies the service that the characteristic belongs to. */ -@property(nonatomic, readonly, nonnull) CBUUID *serviceUUID; +@property(nonatomic, readonly) CBUUID *serviceUUID; /** * The permissions of the characteristic value. @@ -67,3 +70,5 @@ @property(nonatomic, readonly) CBCharacteristicProperties properties; @end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.m index de8dea86..6759b417 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.m @@ -17,10 +17,17 @@ #import #import +NS_ASSUME_NONNULL_BEGIN + @implementation GNCBLEGATTCharacteristic -- (instancetype)initWithUUID:(CBUUID *)characteristicUUID serviceUUID:(CBUUID *)serviceUUID { - return [self initWithUUID:characteristicUUID serviceUUID:serviceUUID permissions:0 properties:0]; +- (instancetype)initWithUUID:(CBUUID *)characteristicUUID + serviceUUID:(CBUUID *)serviceUUID + properties:(CBCharacteristicProperties)properties { + return [self initWithUUID:characteristicUUID + serviceUUID:serviceUUID + permissions:0 + properties:properties]; } - (instancetype)initWithUUID:(CBUUID *)characteristicUUID @@ -38,3 +45,5 @@ } @end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h new file mode 100644 index 00000000..c55017fc --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h @@ -0,0 +1,117 @@ +// 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 +#import + +@class GNCBLEGATTCharacteristic; + +@protocol GNCPeripheral; + +NS_ASSUME_NONNULL_BEGIN + +/** + * A block to be invoked when a call to + * @c discoverCharacteristicsWithUUIDs:serviceUUID:completionHandler: has completed. + * + * @param error On success, error will be @c nil. Otherwise, the error that occurred. + */ +typedef void (^GNCDiscoverCharacteristicsCompletionHandler)(NSError *_Nullable error); + +/** + * A block to be invoked when a call to @c characteristicWithUUID:serviceUUID:completionHandler: has + * completed. + * + * @param characteristic The characteristic or @c nil if an error occurred. + * @param error On success, error will be @c nil. Otherwise, the error that occurred. + */ +typedef void (^GNCGetCharacteristicCompletionHandler)( + GNCBLEGATTCharacteristic *_Nullable characteristic, NSError *_Nullable error); + +/** + * A block to be invoked when a call to @c readValueForCharacteristic:completionHandler: has + * completed. + * + * @param value The characteristic's value or @c nil if an error occurred. + * @param error On success, error will be @c nil. Otherwise, the error that occurred. + */ +typedef void (^GNCReadCharacteristicValueCompletionHandler)(NSData *_Nullable value, + NSError *_Nullable error); + +/** + * An object that can be used to discover, explore, and interact with GATT services and + * characteristics available on a remote peripheral. + * + * @note The public APIs of this class are thread safe. + */ +@interface GNCBLEGATTClient : NSObject + +/** @remark init is not an available initializer. */ +- (instancetype)init NS_UNAVAILABLE; + +/** + * Initializes the GATT client with a specified peripheral. + * + * @param peripheral The peripheral instance. + */ +- (instancetype)initWithPeripheral:(CBPeripheral *)peripheral; + +/** + * Discovers the specified characteristics of a service. + * + * A successful call to this method means the characteristics can be retreived with + * @c characteristicWithUUID:serviceUUID:completionHandler:. + * + * @param characteristics An array of 128-bit UUID objects of the characteristics that you are + * interested in. + * @param serviceUUID A 128-bit UUID that identifies the service that the characteristics belongs + * to. + * @param completionHandler Called on a private queue with @c nil if all characteristics have + * successfully been discovered or else an error. + */ +- (void)discoverCharacteristicsWithUUIDs:(NSArray *)characteristicUUIDs + serviceUUID:(CBUUID *)serviceUUID + completionHandler: + (nullable GNCDiscoverCharacteristicsCompletionHandler)completionHandler; + +/** + * Retrieves a GATT characteristic. + * + * If you haven’t yet called the @c discoverCharacteristicsWithUUIDs:serviceUUID:completionHandler: + * method to discover the characteristic, or if there was an error in doing so, this method will + * complete with an error. + * + * @param characteristicUUID A 128-bit UUID that identifies the characteristic. + * @param serviceUUID A 128-bit UUID that identifies the service that the characteristic belongs to. + * @param completionHandler Called on a private queue with the characteristic if successful or an + * error if one has occured. + */ +- (void)characteristicWithUUID:(CBUUID *)characteristicUUID + serviceUUID:(CBUUID *)serviceUUID + completionHandler:(nullable GNCGetCharacteristicCompletionHandler)completionHandler; + +/** + * Reads the requested characteristic from the associated remote device. + * + * @param characteristic The characteristic whose value you want to read. + * @param completionHandler Called on a private queue with the characteristics value if successful + * or an error if one has occured. + */ +- (void)readValueForCharacteristic:(GNCBLEGATTCharacteristic *)characteristic + completionHandler: + (nullable GNCReadCharacteristicValueCompletionHandler)completionHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m new file mode 100644 index 00000000..21871a87 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m @@ -0,0 +1,299 @@ +// 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/BLEv2/GNCBLEGATTClient.h" + +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" + +NS_ASSUME_NONNULL_BEGIN + +static char *const kGNCBLEGATTClientQueueLabel = "com.nearby.GNCBLEGATTClient"; + +static NSError *InvalidCharacteristicError() { + return [NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorInvalidCharacteristic + userInfo:nil]; +} + +static NSError *AlreadyDiscoveringSpecifiedCharacteristicsError() { + return [NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorAlreadyDiscoveringSpecifiedCharacteristics + userInfo:nil]; +} + +static NSError *AlreadyReadingCharacteristicError() { + return [NSError errorWithDomain:GNCBLEErrorDomain + code:GNCBLEErrorAlreadyReadingCharacteristic + userInfo:nil]; +} + +@interface GNCBLEGATTClient () +@end + +@implementation GNCBLEGATTClient { + dispatch_queue_t _queue; + id _peripheral; + + /** + * A map of service UUIDs with each service holding a map of a list of characterisitcs to a + * completion handler. This is used to track the groupings of characteristic discovery requests. + * When all characteristics of a request are discovered, the completion handler is called and + * removed from the map. + */ + NSMutableDictionary *, + GNCDiscoverCharacteristicsCompletionHandler> *> + *_discoverCharacteristicsCompletionHandlers; + + /** + * A service to characteristic to completion handler map. Used to track pending read requests. + * When a characteristic's value has been updated, the completion handler is called and removed + * from the map. + */ + NSMutableDictionary *> + *_readCharacteristicValueCompletionHandlers; +} + +- (instancetype)initWithPeripheral:(CBPeripheral *)peripheral { + return [self + initWithPeripheral:peripheral + queue:dispatch_queue_create(kGNCBLEGATTClientQueueLabel, DISPATCH_QUEUE_SERIAL)]; +}; + +// Private. +- (instancetype)initWithPeripheral:(id)peripheral + queue:(nullable dispatch_queue_t)queue { + self = [super init]; + if (self) { + _queue = queue ?: dispatch_get_main_queue(); + _peripheral = peripheral; + _peripheral.peripheralDelegate = self; + _discoverCharacteristicsCompletionHandlers = [[NSMutableDictionary alloc] init]; + _readCharacteristicValueCompletionHandlers = [[NSMutableDictionary alloc] init]; + } + return self; +}; + +- (void)discoverCharacteristicsWithUUIDs:(NSArray *)characteristicUUIDs + serviceUUID:(CBUUID *)serviceUUID + completionHandler: + (nullable GNCDiscoverCharacteristicsCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + if (!_discoverCharacteristicsCompletionHandlers[serviceUUID]) { + _discoverCharacteristicsCompletionHandlers[serviceUUID] = [[NSMutableDictionary alloc] init]; + } + // Return an error if we are already discovering the provided characteristics. + if (_discoverCharacteristicsCompletionHandlers[serviceUUID][characteristicUUIDs]) { + if (completionHandler) { + completionHandler(AlreadyDiscoveringSpecifiedCharacteristicsError()); + } + return; + } + // Use the list of characteristic UUIDs as the key. This makes it easy to associate the + // completion handler with the list of characteristics we are waiting for, as well as retrieving + // the complete list of characteristics we need to query for a single service. + _discoverCharacteristicsCompletionHandlers[serviceUUID][characteristicUUIDs] = + completionHandler; + + // Note: A call to @c discoverServices: will always be paired with a delegate call even if the + // service has already been discovered. + [_peripheral discoverServices:@[ serviceUUID ]]; + }); +} + +- (void)characteristicWithUUID:(CBUUID *)characteristicUUID + serviceUUID:(CBUUID *)serviceUUID + completionHandler:(nullable GNCGetCharacteristicCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + CBCharacteristic *characteristic = [self synchronousCharacteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID]; + if (!characteristic) { + if (completionHandler) { + completionHandler(nil, InvalidCharacteristicError()); + } + return; + } + if (completionHandler) { + completionHandler([[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristic.UUID + serviceUUID:characteristic.service.UUID + properties:characteristic.properties], + nil); + } + }); +} + +- (void)readValueForCharacteristic:(GNCBLEGATTCharacteristic *)characteristic + completionHandler: + (nullable GNCReadCharacteristicValueCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + CBCharacteristic *cbCharacteristic = + [self synchronousCharacteristicWithUUID:characteristic.characteristicUUID + serviceUUID:characteristic.serviceUUID]; + + if (!cbCharacteristic) { + if (completionHandler) { + completionHandler(nil, InvalidCharacteristicError()); + } + return; + } + + if (!_readCharacteristicValueCompletionHandlers[characteristic.serviceUUID]) { + _readCharacteristicValueCompletionHandlers[characteristic.serviceUUID] = + [[NSMutableDictionary alloc] init]; + } + // Return an error if we are already discovering the provided characteristics. + if (_readCharacteristicValueCompletionHandlers[characteristic.serviceUUID] + [characteristic.characteristicUUID]) { + if (completionHandler) { + completionHandler(nil, AlreadyReadingCharacteristicError()); + } + return; + } + _readCharacteristicValueCompletionHandlers[characteristic.serviceUUID] + [characteristic.characteristicUUID] = + completionHandler; + + [_peripheral readValueForCharacteristic:cbCharacteristic]; + }); +} + +#pragma mark - Internal + +- (CBCharacteristic *)synchronousCharacteristicWithUUID:(CBUUID *)characteristicUUID + serviceUUID:(CBUUID *)serviceUUID { + dispatch_assert_queue(_queue); + for (CBService *service in _peripheral.services) { + if ([service.UUID isEqual:serviceUUID]) { + for (CBCharacteristic *characteristic in service.characteristics) { + if ([characteristic.UUID isEqual:characteristicUUID]) { + return characteristic; + } + } + } + } + return nil; +} + +#pragma mark - GNCPeripheralDelegate + +- (void)gnc_peripheral:(id)peripheral didDiscoverServices:(nullable NSError *)error { + dispatch_assert_queue(_queue); + // TODO(b/295911088): Queue incoming requests by service would allow us to not attempt + // characteristc discovery on all services and to short circuit and call the completion handler if + // there was an error. + for (CBService *service in peripheral.services) { + // Flatten lists of characteristics for a given service into a single list for discovery. + NSArray *> *groupedCharacteristics = + _discoverCharacteristicsCompletionHandlers[service.UUID].allKeys; + if (!groupedCharacteristics) { + continue; + } + NSMutableSet *flattenedCharacteristics = [[NSMutableSet alloc] init]; + for (NSArray *characteristics in groupedCharacteristics) { + [flattenedCharacteristics addObjectsFromArray:characteristics]; + } + + // Note: Since we don't clear @c _discoverCharacteristicsCompletionHandlers until the + // characterististics have been discovered, multiple calls to + // @c discoverService:characteristics:completionHandler: will cause duplicate characteristic + // discovery calls on the same service. We CANNOT clear the pending list until after we actually + // discover the characteristics, because there can be more than 1 service with the same UUID. + // This is a common occurence for Nearby services, so it should not be treated as an edge case. + [_peripheral discoverCharacteristics:[flattenedCharacteristics allObjects] forService:service]; + } +} + +- (void)gnc_peripheral:(id)peripheral + didDiscoverCharacteristicsForService:(CBService *)service + error:(nullable NSError *)error { + dispatch_assert_queue(_queue); + + // Check if each group of characteristics is a subset of the discovered characteristics. If all + // characteristics of the group have been discovered, call the completion handler with success, + // otherwise continue waiting. If the characteristic discovery returns an error, call all pending + // discovery request completion handlers with the error. + NSMutableSet *characteristics = [[NSMutableSet alloc] init]; + for (CBCharacteristic *characteristic in service.characteristics) { + [characteristics addObject:characteristic.UUID]; + } + [_discoverCharacteristicsCompletionHandlers[service.UUID].copy + enumerateKeysAndObjectsUsingBlock:^(NSArray *pendingCharacteristics, + GNCDiscoverCharacteristicsCompletionHandler handler, + BOOL *stop) { + if ([[NSSet setWithArray:pendingCharacteristics] isSubsetOfSet:characteristics]) { + _discoverCharacteristicsCompletionHandlers[service.UUID][pendingCharacteristics] = nil; + handler(nil); + return; + } + + // TODO(b/295911088): Queue incoming requests by service to avoid this issue. + // This could be a race, where @c discoverService:characteristics:completionHandler: is + // called multiple times quickly for the same service. If a request fails, all pending + // requests for the same service are also failed. + if (error) { + _discoverCharacteristicsCompletionHandlers[service.UUID][pendingCharacteristics] = nil; + handler(error); + return; + } + }]; +} + +- (void)gnc_peripheral:(id)peripheral + didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic + error:(nullable NSError *)error { + dispatch_assert_queue(_queue); + NSMutableDictionary *handlers = + _readCharacteristicValueCompletionHandlers[characteristic.service.UUID]; + if (!handlers) { + return; + } + GNCReadCharacteristicValueCompletionHandler handler = handlers[characteristic.UUID]; + handlers[characteristic.UUID] = nil; + if (handler) { + handler(characteristic.value, error); + } +} + +#pragma mark - CBPeripheralDelegate + +- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error { + dispatch_async(_queue, ^{ + [self gnc_peripheral:peripheral didDiscoverServices:error]; + }); +} + +- (void)peripheral:(CBPeripheral *)peripheral + didDiscoverCharacteristicsForService:(CBService *)service + error:(nullable NSError *)error { + dispatch_async(_queue, ^{ + [self gnc_peripheral:peripheral didDiscoverCharacteristicsForService:service error:error]; + }); +} + +- (void)peripheral:(CBPeripheral *)peripheral + didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic + error:(nullable NSError *)error { + dispatch_async(_queue, ^{ + [self gnc_peripheral:peripheral didUpdateValueForCharacteristic:characteristic error:error]; + }); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h index 6408be13..2461d5f6 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h @@ -41,8 +41,8 @@ typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error); * @param characteristicUUID A 128-bit UUID that identifies the characteristic. * @param permissions The permissions of the characteristic value. * @param properties The properties of the characteristic. - * @param completionHandler Called on the main queue with the characteristic if successfully created - * or an error if one has occured. + * @param completionHandler Called on a private queue with the characteristic if successfully + * created or an error if one has occured. */ - (void)createCharacteristicWithServiceID:(CBUUID *)serviceUUID characteristicUUID:(CBUUID *)characteristicUUID @@ -56,8 +56,8 @@ typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error); * * @param characteristic The characteristic to update. * @param value The new value for the characteristic. - * @param completionHandler Called on the main queue with @c nil if successfully updated or an error - * if one has occured. + * @param completionHandler Called on a private queue with @c nil if successfully updated or an + * error if one has occured. */ - (void)updateCharacteristic:(GNCBLEGATTCharacteristic *)characteristic value:(nullable NSData *)value @@ -76,8 +76,8 @@ typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error); * longer than 22 bytes. This also means we can only support advertising a single service. * * @param serviceData A dictionary that contains service-specific advertisement data. - * @param completionHandler Called on the main queue with @c nil if successfully started advertising - * or an error if one has occured. + * @param completionHandler Called on a private queue with @c nil if successfully started + * advertising or an error if one has occured. */ - (void)startAdvertisingData:(NSDictionary *)serviceData completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler; diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m index 996c8396..2cd60e63 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m @@ -27,7 +27,7 @@ NS_ASSUME_NONNULL_BEGIN static char *const kGNCBLEGATTServerQueueLabel = "com.nearby.GNCBLEGATTServer"; -@interface GNCBLEGATTServer () +@interface GNCBLEGATTServer () @end @implementation GNCBLEGATTServer { diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h new file mode 100644 index 00000000..d3a7a1f8 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h @@ -0,0 +1,167 @@ +// 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 +#import + +@protocol GNCPeripheralDelegate; + +NS_ASSUME_NONNULL_BEGIN + +/** Protocol which helps create a fake of a @c CBPeripheral to inject for testing. */ +@protocol GNCPeripheral + +// This can't be @c delegate, because it would shadow @c CBPeripheral's delegate. +/** + * The peripheral's delegate. + * + * This delegate is only sent CBPeripheralDelegate messages, and the delegate is responsible for + * forwarding those messages to its GNCPeripheralDelegate method implementations. + * + * See: https://developer.apple.com/videos/play/wwdc2018/417/ + */ +@property(weak, nonatomic, nullable) id peripheralDelegate; + +/** + * A list of a peripheral’s discovered services. + * + * Returns an array of services (represented by CBService objects) that successful a call to the + * @c discoverServices: method discovered. If you haven’t yet called the @c discoverServices: method + * to discover the services of the peripheral, or if there was an error in doing so, the value of + * this property is @c nil. + */ +@property(retain, readonly, nullable) NSArray *services; + +/** + * Discovers the specified services of the peripheral. + * + * You can provide an array of CBUUID objects, representing service UUIDs, in the @c serviceUUIDs + * parameter. When you do, the peripheral returns only the services of the peripheral that match the + * provided UUIDs. + * + * @note If the @c serviceUUIDs parameter is @c nil, this method returns all of the peripheral’s + * available services. This is much slower than providing an array of service UUIDs to search for. + * + * When the peripheral discovers one or more services, it calls the + * @c peripheral:didDiscoverServices: method of its delegate object. After a peripheral discovers + * services, you can access them through the peripheral’s @c services property. + * + * @param serviceUUIDs An array of CBUUID objects that you are interested in. Each CBUUID object + * represents a UUID that identifies the type of service you want to discover. + */ +- (void)discoverServices:(nullable NSArray *)serviceUUIDs; + +/** + * Discovers the specified characteristics of a service. + * + * You can provide an array of CBUUID objects, representing characteristic UUIDs, in the + * @c characteristicUUIDs parameter. When you do, the peripheral returns only the characteristics of + * the service that match the provided UUIDs. If the @c characteristicUUIDs parameter is @c nil, + * this method returns all characteristics of the service. + * + * @note If the @c characteristicUUIDs parameter is @c nil, this method returns all of the service’s + * characteristics. This is much slower than providing an array of characteristic UUIDs to search + * for. + * + * When the peripheral discovers one or more characteristics of the specified service, it calls the + * @c peripheral:didDiscoverCharacteristicsForService:error: method of its delegate object. After + * the peripheral discovers the service’s characteristics, you can access them through the service’s + * @c characteristics property. + * + * @param characteristicUUIDs An array of CBUUID objects that you are interested in. Each CBUUID + * object represents a UUID that identifies the type of a characteristic + * you want to discover. + * @param service The service whose characteristics you want to discover. + */ +- (void)discoverCharacteristics:(nullable NSArray *)characteristicUUIDs + forService:(CBService *)service; + +/** + * Retrieves the value of a specified characteristic. + * + * When you call this method to read the value of a characteristic, the peripheral calls the + * @c peripheral:didUpdateValueForCharacteristic:error: method of its delegate object. If the + * peripheral successfully reads the value of the characteristic, you can access it through the + * characteristic’s @c value property. + * + * Not all characteristics have a readable value. You can determine whether a characteristic’s value + * is readable by accessing the relevant properties of the CBCharacteristicProperties enumeration. + * + * @param characteristic The characteristic whose value you want to read. + */ +- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic; + +@end + +/** + * Protocol which helps the @c GNCPeripheral wrap a @c CBPeripheralDelegate for + * testing. + */ +@protocol GNCPeripheralDelegate + +/** + * Tells the delegate that peripheral service discovery succeeded. + * + * Called when your app calls the @c discoverServices: method. If the peripheral successfully + * discovers services, you can access them through the peripheral’s @c services property. If + * successful, the @c error parameter is @c nil. If unsuccessful, the @c error parameter returns the + * cause of the failure. + * + * @param peripheral The peripheral to which the services belong. + * @param error The reason the call failed, or @c nil if no error occurred. + */ +- (void)gnc_peripheral:(id)peripheral didDiscoverServices:(nullable NSError *)error; + +/** + * Tells the delegate that the peripheral found characteristics for a service. + * + * Called when your app calls the @c discoverCharacteristics:forService: method. If the peripheral + * successfully discovers the characteristics of the specified service, you can access them through + * the service’s @c characteristics property. If successful, the @c error parameter is @c nil. If + * unsuccessful, the @c error parameter returns the cause of the failure. + * + * @param peripheral The peripheral providing this information. + * @param service The service to which the characteristics belong. + * @param error The reason the call failed, or @c nil if no error occurred. + */ +- (void)gnc_peripheral:(id)peripheral + didDiscoverCharacteristicsForService:(CBService *)service + error:(nullable NSError *)error; + +/** + * Tells the delegate that retrieving the specified characteristic’s value succeeded, or that the + * characteristic’s value changed. + * + * Called when your app calls the @c readValueForCharacteristic: method. If successful, the @c error + * parameter is @c nil. If unsuccessful, the @c error parameter returns the cause of the failure. + * + * @param peripheral The peripheral providing this information. + * @param characteristic The characteristic containing the value. + * @param error The reason the call failed, or @c nil if no error occurred. + */ +- (void)gnc_peripheral:(id)peripheral + didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic + error:(nullable NSError *)error; + +@end + +/** + * Declares that @c CBPeripheral implements the @c GNCPeripheral protocol. + * + * This allows us to directly use a @c CBPeripheral as a @c GNCPeripheral. + */ +@interface CBPeripheral () +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.m new file mode 100644 index 00000000..69923133 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.m @@ -0,0 +1,34 @@ +// 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/BLEv2/GNCPeripheral.h" + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@implementation CBPeripheral (GNCPeripheralAdditions) + +- (void)setPeripheralDelegate:(nullable id)peripheralDelegate { + self.delegate = peripheralDelegate; +} + +- (nullable id)peripheralDelegate { + return (id)self.delegate; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h index 5b0aded4..ecbeb887 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.h @@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN @protocol GNCPeripheralManager /** Shadow property of a @c CBPeripheralManagerDelegate. */ -@property(nonatomic, nullable) id peripheralDelegate; +@property(weak, nonatomic, nullable) id peripheralDelegate; @property(nonatomic, assign, readonly) CBManagerState state; @@ -124,7 +124,7 @@ NS_ASSUME_NONNULL_BEGIN * Protocol which helps the @c GNCPeripheralManager wrap a @c CBPeripheralManagerDelegate for * testing. */ -@protocol GNCPeripheralManagerDelegate +@protocol GNCPeripheralManagerDelegate /** * Tells the delegate the peripheral manager’s state updated. diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.m index cf7eecbd..784b6b76 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheralManager.m @@ -23,9 +23,7 @@ NS_ASSUME_NONNULL_BEGIN @implementation CBPeripheralManager (GNCPeripheralManagerAdditions) - (void)setPeripheralDelegate:(nullable id)peripheralDelegate { - NSAssert([peripheralDelegate conformsToProtocol:@protocol(CBPeripheralManagerDelegate)], - @"peripheralDelegate must conform to protocol CBPeripheralManagerDelegate"); - self.delegate = (id)peripheralDelegate; + self.delegate = peripheralDelegate; } - (nullable id)peripheralDelegate { diff --git a/internal/platform/implementation/apple/Mediums/BUILD b/internal/platform/implementation/apple/Mediums/BUILD index 459b548c..aef72927 100644 --- a/internal/platform/implementation/apple/Mediums/BUILD +++ b/internal/platform/implementation/apple/Mediums/BUILD @@ -20,7 +20,9 @@ objc_library( srcs = [ "BLEv2/GNCBLEError.m", "BLEv2/GNCBLEGATTCharacteristic.m", + "BLEv2/GNCBLEGATTClient.m", "BLEv2/GNCBLEGATTServer.m", + "BLEv2/GNCPeripheral.m", "BLEv2/GNCPeripheralManager.m", "BLEv2/NSData+GNCWebSafeBase64.m", "Ble/GNCMBleCentral.m", @@ -40,7 +42,9 @@ objc_library( hdrs = [ "BLEv2/GNCBLEError.h", "BLEv2/GNCBLEGATTCharacteristic.h", + "BLEv2/GNCBLEGATTClient.h", "BLEv2/GNCBLEGATTServer.h", + "BLEv2/GNCPeripheral.h", "BLEv2/GNCPeripheralManager.h", "BLEv2/NSData+GNCWebSafeBase64.h", "Ble/GNCMBleCentral.h", diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index a14ce29d..864188a5 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -24,12 +24,15 @@ objc_library( testonly = True, srcs = [ "GNCBLEGATTCharacteristicTest.mm", + "GNCBLEGATTClientTest.m", "GNCBLEGATTServer+Testing.h", "GNCBLEGATTServerTest.m", "GNCBLEUtilsTest.mm", "GNCBleTest.mm", "GNCBluetoothAdapterTest.mm", "GNCCryptoTest.mm", + "GNCFakePeripheral.h", + "GNCFakePeripheral.m", "GNCFakePeripheralManager.h", "GNCFakePeripheralManager.m", "GNCIPAddressTest.mm", @@ -40,6 +43,7 @@ objc_library( "NSData+GNCWebSafeBase64Test.m", ], deps = [ + ":GNCBLEGATTClient_Testing", "//internal/platform:base", "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", @@ -54,6 +58,15 @@ objc_library( ], ) +objc_library( + name = "GNCBLEGATTClient_Testing", + hdrs = ["GNCBLEGATTClient+Testing.h"], + deps = [ + "//internal/platform/implementation/apple/Mediums", + "//third_party/apple_frameworks:Foundation", + ], +) + ios_unit_test( name = "PlatformTests", minimum_os_version = IOS_MINIMUM_OS, diff --git a/internal/platform/implementation/apple/Tests/GNCBLEGATTCharacteristicTest.mm b/internal/platform/implementation/apple/Tests/GNCBLEGATTCharacteristicTest.mm index c7b99b88..4aa8c827 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEGATTCharacteristicTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCBLEGATTCharacteristicTest.mm @@ -26,12 +26,15 @@ - (void)testConvenienceInit { CBUUID *characteristicUUID = [CBUUID UUIDWithString:@"00000000-0000-3000-8000-000000000000"]; CBUUID *serviceUUID = [CBUUID UUIDWithString:@"0000FEF3-0000-1000-8000-00805F9B34FB"]; + CBCharacteristicProperties properties = CBCharacteristicPropertyRead; GNCBLEGATTCharacteristic *characteristic = - [[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristicUUID serviceUUID:serviceUUID]; + [[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristicUUID + serviceUUID:serviceUUID + properties:properties]; XCTAssertEqualObjects(characteristic.characteristicUUID, characteristicUUID); XCTAssertEqualObjects(characteristic.serviceUUID, serviceUUID); XCTAssertEqual(characteristic.permissions, 0); - XCTAssertEqual(characteristic.properties, 0); + XCTAssertEqual(characteristic.properties, properties); } - (void)testInit { diff --git a/internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h b/internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h new file mode 100644 index 00000000..a3a52f65 --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h @@ -0,0 +1,40 @@ +// THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_TESTS_GNCBLEGATTCLIENT_TESTING_H_ +// 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/BLEv2/GNCBLEGATTClient.h" + +#import + +@protocol GNCPeripheral; + +NS_ASSUME_NONNULL_BEGIN + +@interface GNCBLEGATTClient (Testing) + +/** + * Creates a GATT client with a provided peripheral. + * + * This is only exposed for testing and can be used to inject a fake peripheral. + * + * @param peripheral The peripheral instance. + * @param queue The queue to run on, this must match the queue that the peripheral's delegate is + * running on. Defaults to the main queue when @c nil. + */ +- (instancetype)initWithPeripheral:(id)peripheral + queue:(nullable dispatch_queue_t)queue; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Tests/GNCBLEGATTClientTest.m b/internal/platform/implementation/apple/Tests/GNCBLEGATTClientTest.m new file mode 100644 index 00000000..b53cdef7 --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCBLEGATTClientTest.m @@ -0,0 +1,748 @@ +// 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/BLEv2/GNCBLEGATTClient.h" + +#import +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" +#import "internal/platform/implementation/apple/Tests/GNCBLEGATTClient+Testing.h" +#import "internal/platform/implementation/apple/Tests/GNCFakePeripheral.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"; +static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-000000000001"; + +@interface GNCBLEGATTClientTest : XCTestCase +@end + +@implementation GNCBLEGATTClientTest + +#pragma mark - Discover Characteristics + +- (void)testDiscoverCharacteristics { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *discoverCharacteristicsExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Discover characteristics."]; + + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [discoverCharacteristicsExpectation fulfill]; + }]; + + [self waitForExpectations:@[ discoverCharacteristicsExpectation ] timeout:3]; + + XCTAssertEqualObjects(fakePeripheral.services[0].UUID, serviceUUID); + XCTAssertEqualObjects(fakePeripheral.services[0].characteristics[0].UUID, characteristicUUID); +} + +- (void)testDiscoverCharacteristicsWithServiceDiscoveryError { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + fakePeripheral.discoverServicesError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *discoverCharacteristicsExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Discover characteristics."]; + + // TODO(b/295911088): Failed discovery of services won't trigger the completionHandler until we + // implement a service queue. + discoverCharacteristicsExpectation.inverted = YES; + + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + // TODO(b/295911088): Make assertions when service queue is + // implemented. + [discoverCharacteristicsExpectation fulfill]; + }]; + + [self waitForExpectations:@[ discoverCharacteristicsExpectation ] timeout:3]; + + XCTAssertEqual(fakePeripheral.services.count, 0); +} + +- (void)testDiscoverCharacteristicsWithCharacteristicDiscoveryError { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + fakePeripheral.discoverCharacteristicsForServiceError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *discoverCharacteristicsExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Discover characteristics."]; + + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + XCTAssertNotNil(error); + [discoverCharacteristicsExpectation fulfill]; + }]; + + [self waitForExpectations:@[ discoverCharacteristicsExpectation ] timeout:3]; + + XCTAssertEqualObjects(fakePeripheral.services[0].UUID, serviceUUID); + XCTAssertEqual(fakePeripheral.services[0].characteristics.count, 0); +} + +// TODO(b/295911088): When service queue is implemented, this is expected to not be an error. +- (void)testDuplicateDiscoverCharacteristics { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *discoverCharacteristics1Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Discover characteristics 1."]; + XCTestExpectation *discoverCharacteristics2Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Discover characteristics 2."]; + + fakePeripheral.delegateDelay = 1; + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [discoverCharacteristics1Expectation fulfill]; + }]; + + // Queue the delay change so it doesn't immediately overwrite the delay set for the previous + // operation. + dispatch_async(dispatch_get_main_queue(), ^{ + fakePeripheral.delegateDelay = 0; + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + XCTAssertNotNil(error); + [discoverCharacteristics2Expectation fulfill]; + }]; + }); + + [self waitForExpectations:@[ + discoverCharacteristics1Expectation, discoverCharacteristics2Expectation + ] + timeout:3]; +} + +- (void)testDiscoverCharacteristicsMultipleCallsWithDifferentServices { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID1 = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *serviceUUID2 = [CBUUID UUIDWithString:kServiceUUID2]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *discoverCharacteristics1Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Discover characteristics 1."]; + XCTestExpectation *discoverCharacteristics2Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Discover characteristics 2."]; + + fakePeripheral.delegateDelay = 1; + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID1 + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [discoverCharacteristics1Expectation fulfill]; + }]; + + // Queue the delay change so it doesn't immediately overwrite the delay set for the previous + // operation. + dispatch_async(dispatch_get_main_queue(), ^{ + fakePeripheral.delegateDelay = 0; + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID2 + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [discoverCharacteristics2Expectation fulfill]; + }]; + }); + + [self waitForExpectations:@[ + discoverCharacteristics1Expectation, discoverCharacteristics2Expectation + ] + timeout:3]; + + XCTAssertEqualObjects(fakePeripheral.services[0].UUID, serviceUUID2); + XCTAssertEqualObjects(fakePeripheral.services[0].characteristics[0].UUID, characteristicUUID); + XCTAssertEqualObjects(fakePeripheral.services[1].UUID, serviceUUID1); + XCTAssertEqualObjects(fakePeripheral.services[1].characteristics[0].UUID, characteristicUUID); +} + +- (void)testDiscoverCharacteristicsMultipleCallsWithDifferentCharacteristics { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID1 = [CBUUID UUIDWithString:kCharacteristicUUID1]; + CBUUID *characteristicUUID2 = [CBUUID UUIDWithString:kCharacteristicUUID2]; + + XCTestExpectation *discoverCharacteristics1Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Discover characteristics 1."]; + XCTestExpectation *discoverCharacteristics2Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Discover characteristics 2."]; + + fakePeripheral.delegateDelay = 1; + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID1 ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [discoverCharacteristics1Expectation fulfill]; + }]; + + // Queue the delay change so it doesn't immediately overwrite the delay set for the previous + // operation. + dispatch_async(dispatch_get_main_queue(), ^{ + fakePeripheral.delegateDelay = 0; + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID2 ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + XCTAssertNil(error); + [discoverCharacteristics2Expectation fulfill]; + }]; + }); + + [self waitForExpectations:@[ + discoverCharacteristics1Expectation, discoverCharacteristics2Expectation + ] + timeout:3]; + + XCTAssertEqualObjects(fakePeripheral.services[0].UUID, serviceUUID); + XCTAssertEqualObjects(fakePeripheral.services[0].characteristics[0].UUID, characteristicUUID2); + XCTAssertEqualObjects(fakePeripheral.services[0].characteristics[1].UUID, characteristicUUID1); +} + +#pragma mark - Get Characteristic + +- (void)testGetCharacteristic { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *characteristicExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Get characteristic."]; + + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [gattClient characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^( + GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(characteristic); + [characteristicExpectation fulfill]; + }]; + }]; + + [self waitForExpectations:@[ characteristicExpectation ] timeout:3]; +} + +- (void)testGetCharacteristicWithServiceDiscoveryError { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + fakePeripheral.discoverServicesError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *characteristicExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Get characteristic."]; + + // TODO(b/295911088): Failed discovery of services won't trigger the completionHandler until we + // implement a service queue. + characteristicExpectation.inverted = YES; + + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [gattClient characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^( + GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + // TODO(b/295911088): Make assertions when service + // queue is implemented. + [characteristicExpectation fulfill]; + }]; + }]; + + [self waitForExpectations:@[ characteristicExpectation ] timeout:3]; +} + +- (void)testGetCharacteristicWithCharacteristicDiscoveryError { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + fakePeripheral.discoverCharacteristicsForServiceError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *characteristicExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Get characteristic."]; + + [gattClient discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [gattClient characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^( + GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + XCTAssertNotNil(error); + XCTAssertNil(characteristic); + [characteristicExpectation fulfill]; + }]; + }]; + + [self waitForExpectations:@[ characteristicExpectation ] timeout:3]; +} + +- (void)testDuplicateGetCharacteristic { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *characteristic1Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Get characteristic 1."]; + XCTestExpectation *characteristic2Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Get characteristic 2."]; + + [gattClient + discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + fakePeripheral.delegateDelay = 1; + [gattClient + characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(characteristic); + [characteristic1Expectation fulfill]; + }]; + // Queue the delay change so it doesn't immediately overwrite the delay set + // for the previous operation. + dispatch_async(dispatch_get_main_queue(), ^{ + fakePeripheral.delegateDelay = 0; + [gattClient + characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(characteristic); + [characteristic2Expectation fulfill]; + }]; + }); + }]; + + [self waitForExpectations:@[ characteristic1Expectation, characteristic2Expectation ] timeout:3]; +} + +- (void)testGetNonExistentCharacteristic { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *characteristicExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Get characteristic."]; + + [gattClient characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, NSError *error) { + XCTAssertNotNil(error); + XCTAssertNil(characteristic); + [characteristicExpectation fulfill]; + }]; + + [self waitForExpectations:@[ characteristicExpectation ] timeout:3]; +} + +#pragma mark - Read Value for Characteristic + +- (void)testReadValueForCharacteristic { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *readValueForCharacteristicExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Read value for characteristic."]; + + [gattClient + discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [gattClient + characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + [gattClient + readValueForCharacteristic:characteristic + completionHandler:^(NSData *value, NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(value); + [readValueForCharacteristicExpectation fulfill]; + }]; + }]; + }]; + + [self waitForExpectations:@[ readValueForCharacteristicExpectation ] timeout:3]; +} + +- (void)testReadValueForCharacteristicWithServiceDiscoveryError { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + fakePeripheral.discoverServicesError = [NSError errorWithDomain:@"fake" code:0 userInfo:nil]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *readValueForCharacteristicExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Read value for characteristic."]; + + // TODO(b/295911088): Failed discovery of services won't trigger the completionHandler until we + // implement a service queue. + readValueForCharacteristicExpectation.inverted = YES; + + [gattClient + discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [gattClient + characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + [gattClient + readValueForCharacteristic:characteristic + completionHandler:^(NSData *value, NSError *error) { + // TODO(b/295911088): Make assertions when service + // queue is implemented. + [readValueForCharacteristicExpectation fulfill]; + }]; + }]; + }]; + + [self waitForExpectations:@[ readValueForCharacteristicExpectation ] timeout:3]; +} + +- (void)testReadValueForCharacteristicWithCharacteristicDiscoveryError { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + fakePeripheral.discoverCharacteristicsForServiceError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *readValueForCharacteristicExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Read value for characteristic."]; + + [gattClient + discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [gattClient + characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + [gattClient + readValueForCharacteristic:characteristic + completionHandler:^(NSData *value, NSError *error) { + XCTAssertNotNil(error); + XCTAssertNil(value); + [readValueForCharacteristicExpectation fulfill]; + }]; + }]; + }]; + + [self waitForExpectations:@[ readValueForCharacteristicExpectation ] timeout:3]; +} + +- (void)testReadValueForCharacteristicWithReadValueForCharacteristicError { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + fakePeripheral.readValueForCharacteristicError = [NSError errorWithDomain:@"fake" + code:0 + userInfo:nil]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *readValueForCharacteristicExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Read value for characteristic."]; + + [gattClient + discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [gattClient + characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + [gattClient + readValueForCharacteristic:characteristic + completionHandler:^(NSData *value, NSError *error) { + XCTAssertNotNil(error); + XCTAssertNil(value); + [readValueForCharacteristicExpectation fulfill]; + }]; + }]; + }]; + + [self waitForExpectations:@[ readValueForCharacteristicExpectation ] timeout:3]; +} + +// TODO(b/295911088): When service queue is implemented, this is expected to not be an error. +- (void)testDuplicateReadValueForCharacteristic { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + XCTestExpectation *readValueForCharacteristic1Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Read value for characteristic 1."]; + XCTestExpectation *readValueForCharacteristic2Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Read value for characteristic 2."]; + + [gattClient + discoverCharacteristicsWithUUIDs:@[ characteristicUUID ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [gattClient + characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + fakePeripheral.delegateDelay = 1; + [gattClient + readValueForCharacteristic:characteristic + completionHandler:^(NSData *value, NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(value); + [readValueForCharacteristic1Expectation fulfill]; + }]; + + // Queue the delay change so it doesn't immediately overwrite the + // delay set for the previous operation. + dispatch_async(dispatch_get_main_queue(), ^{ + fakePeripheral.delegateDelay = 0; + [gattClient + readValueForCharacteristic:characteristic + completionHandler:^(NSData *value, + NSError *error) { + XCTAssertNotNil(error); + XCTAssertNil(value); + [readValueForCharacteristic2Expectation fulfill]; + }]; + }); + }]; + }]; + + [self waitForExpectations:@[ + readValueForCharacteristic1Expectation, readValueForCharacteristic2Expectation + ] + timeout:3]; +} + +- (void)testReadValueForMultipleCharacteristics { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID1 = [CBUUID UUIDWithString:kCharacteristicUUID1]; + CBUUID *characteristicUUID2 = [CBUUID UUIDWithString:kCharacteristicUUID2]; + + XCTestExpectation *readValueForCharacteristic1Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Read value for characteristic 1."]; + XCTestExpectation *readValueForCharacteristic2Expectation = + [[XCTestExpectation alloc] initWithDescription:@"Read value for characteristic 2."]; + + [gattClient + discoverCharacteristicsWithUUIDs:@[ characteristicUUID1, characteristicUUID2 ] + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [gattClient + characteristicWithUUID:characteristicUUID1 + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + fakePeripheral.delegateDelay = 1; + [gattClient + readValueForCharacteristic:characteristic + completionHandler:^(NSData *value, NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(value); + [readValueForCharacteristic1Expectation fulfill]; + }]; + }]; + + [gattClient + characteristicWithUUID:characteristicUUID2 + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, + NSError *error) { + // Queue the delay change so it doesn't immediately overwrite the + // delay set for the previous operation. + dispatch_async(dispatch_get_main_queue(), ^{ + fakePeripheral.delegateDelay = 0; + [gattClient + readValueForCharacteristic:characteristic + completionHandler:^(NSData *value, + NSError *error) { + XCTAssertNil(error); + XCTAssertNotNil(value); + [readValueForCharacteristic2Expectation fulfill]; + }]; + }); + }]; + }]; + + [self waitForExpectations:@[ + readValueForCharacteristic1Expectation, readValueForCharacteristic2Expectation + ] + timeout:3]; +} + +- (void)testReadValueForUndiscoveredCharacteristic { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + GNCBLEGATTCharacteristic *characteristic = + [[GNCBLEGATTCharacteristic alloc] initWithUUID:characteristicUUID + serviceUUID:serviceUUID + properties:CBCharacteristicPropertyRead]; + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Read value for characteristic."]; + + [gattClient readValueForCharacteristic:characteristic + completionHandler:^(NSData *value, NSError *error) { + XCTAssertNotNil(error); + XCTAssertNil(value); + [expectation fulfill]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; +} + +#pragma mark - Delegate Calls + +- (void)testUnexpectedDelegateCalls { + GNCFakePeripheral *fakePeripheral = [[GNCFakePeripheral alloc] init]; + + GNCBLEGATTClient *gattClient = [[GNCBLEGATTClient alloc] initWithPeripheral:fakePeripheral + queue:nil]; + + CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID1]; + CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID1]; + + [fakePeripheral.peripheralDelegate gnc_peripheral:fakePeripheral didDiscoverServices:nil]; + + [fakePeripheral.peripheralDelegate + gnc_peripheral:fakePeripheral + didDiscoverCharacteristicsForService:[[CBMutableService alloc] initWithType:serviceUUID + primary:YES] + error:nil]; + + [fakePeripheral.peripheralDelegate gnc_peripheral:fakePeripheral + didUpdateValueForCharacteristic:[[CBMutableCharacteristic alloc] + initWithType:characteristicUUID + properties:0 + value:nil + permissions:0] + error:nil]; + + // Test to make sure unexpected delegate calls don't cause any issues from missing handlers. + XCTAssertNotNil(gattClient); +} + +@end diff --git a/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m b/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m index 0a3da269..a016a47f 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m +++ b/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h" #import #import #import -#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" #import "internal/platform/implementation/apple/Tests/GNCBLEGATTServer+Testing.h" #import "internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.h" diff --git a/internal/platform/implementation/apple/Tests/GNCFakePeripheral.h b/internal/platform/implementation/apple/Tests/GNCFakePeripheral.h new file mode 100644 index 00000000..0d62478d --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCFakePeripheral.h @@ -0,0 +1,60 @@ +// 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 +#import + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" + +NS_ASSUME_NONNULL_BEGIN + +/** A fake implementation of @c GNCPeripheral to inject for testing. */ +@interface GNCFakePeripheral : NSObject + +/** + * Similates a @c discoverServices: error. + * + * Setting this error to a value other than @c nil will simulate a failure when calling + * @c discoverServices: and will call the + * @c gnc_peripheral:didDiscoverServices: delegate method with the provided + * error. + */ +@property(nonatomic, nullable, readwrite) NSError *discoverServicesError; + +/** + * Similates a @c discoverCharacteristics:forService: error. + * + * Setting this error to a value other than @c nil will simulate a failure when calling + * @c discoverCharacteristics:forService: and will call the + * @c gnc_peripheral:didDiscoverCharacteristicsForService:error: delegate method with the provided + * error. + */ +@property(nonatomic, nullable, readwrite) NSError *discoverCharacteristicsForServiceError; + +/** + * Similates a @c readValueForCharacteristic: error. + * + * Setting this error to a value other than @c nil will simulate a failure when calling + * @c discoverCharacteristics:forService: and will call the + * @c gnc_peripheral:didUpdateValueForCharacteristic:error: delegate method with the provided + * error. + */ +@property(nonatomic, nullable, readwrite) NSError *readValueForCharacteristicError; + +/** Similates a delay in all delegate calls by the specified amount. */ +@property(nonatomic, readwrite) NSTimeInterval delegateDelay; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Tests/GNCFakePeripheral.m b/internal/platform/implementation/apple/Tests/GNCFakePeripheral.m new file mode 100644 index 00000000..1fa97973 --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCFakePeripheral.m @@ -0,0 +1,118 @@ +// 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/Tests/GNCFakePeripheral.h" + +#import +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface CBService () + +// Change property to readwrite for tests. +@property(retain, readwrite, nullable) NSArray *characteristics; + +@end + +@interface CBCharacteristic () + +// Change property to readwrite for tests. +@property(retain, readwrite, nullable) NSData *value; + +@end + +@implementation GNCFakePeripheral { + NSMutableArray *_services; +} + +@synthesize peripheralDelegate; + +- (instancetype)init { + self = [super init]; + if (self) { + _services = [[NSMutableArray alloc] init]; + } + return self; +} + +- (nullable NSArray *)services { + return _services; +} + +- (void)discoverServices:(nullable NSArray *)serviceUUIDs { + [self delayDelegateUsingBlock:^() { + if (!_discoverServicesError) { + for (CBUUID *serviceUUID in serviceUUIDs) { + [_services addObject:[[CBMutableService alloc] initWithType:serviceUUID primary:YES]]; + } + } + + [peripheralDelegate gnc_peripheral:self didDiscoverServices:_discoverServicesError]; + }]; +} + +- (void)discoverCharacteristics:(nullable NSArray *)characteristicUUIDs + forService:(CBService *)service { + [self delayDelegateUsingBlock:^() { + if (!_discoverCharacteristicsForServiceError) { + NSMutableArray *characteristics = service.characteristics.mutableCopy; + if (!characteristics) { + characteristics = [NSMutableArray array]; + } + for (CBUUID *characteristicUUID in characteristicUUIDs) { + [characteristics addObject:[[CBMutableCharacteristic alloc] + initWithType:characteristicUUID + properties:CBCharacteristicPropertyRead + value:nil + permissions:CBAttributePermissionsReadable]]; + } + service.characteristics = characteristics; + } + + [peripheralDelegate gnc_peripheral:self + didDiscoverCharacteristicsForService:service + error:_discoverCharacteristicsForServiceError]; + }]; +} + +- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic { + [self delayDelegateUsingBlock:^() { + if (!_readValueForCharacteristicError) { + characteristic.value = [NSData data]; + } + + [peripheralDelegate gnc_peripheral:self + didUpdateValueForCharacteristic:characteristic + error:_readValueForCharacteristicError]; + }]; +} + +- (void)delayDelegateUsingBlock:(void (^)())block { + if (_delegateDelay <= 0) { + block(); + } else { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, _delegateDelay * NSEC_PER_SEC), + dispatch_get_main_queue(), ^{ + block(); + }); + } +} + +@end + +NS_ASSUME_NONNULL_END From 07a94a950dbb5ead7a770f89ca617504c0ca2f65 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Thu, 17 Aug 2023 13:47:34 -0700 Subject: [PATCH 106/128] Add Objective-C++ GATT client wrapper PiperOrigin-RevId: 557925490 --- Package.swift | 1 + internal/platform/implementation/apple/BUILD | 2 + .../implementation/apple/ble_gatt_client.h | 88 ++++++++++++ .../implementation/apple/ble_gatt_client.mm | 134 ++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 internal/platform/implementation/apple/ble_gatt_client.h create mode 100644 internal/platform/implementation/apple/ble_gatt_client.mm diff --git a/Package.swift b/Package.swift index 5f66797b..8649eba4 100644 --- a/Package.swift +++ b/Package.swift @@ -573,6 +573,7 @@ let package = Package( // Temporarily ignore BLEv2 source files. // TODO(b/293283024): Stop ignoring these files when BLEv2 migration is complete. "internal/platform/implementation/apple/ble_gatt_server.mm", + "internal/platform/implementation/apple/ble_gatt_client.mm", "internal/platform/implementation/apple/ble_peripheral.mm", "internal/platform/implementation/apple/ble_server_socket.mm", "internal/platform/implementation/apple/ble_socket.mm", diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 0bd4ef8d..6721444a 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -107,6 +107,7 @@ objc_library( objc_library( name = "ble_v2", srcs = [ + "ble_gatt_client.mm", "ble_gatt_server.mm", "ble_peripheral.mm", "ble_server_socket.mm", @@ -116,6 +117,7 @@ objc_library( "utils.mm", ], hdrs = [ + "ble_gatt_client.h", "ble_gatt_server.h", "ble_peripheral.h", "ble_server_socket.h", diff --git a/internal/platform/implementation/apple/ble_gatt_client.h b/internal/platform/implementation/apple/ble_gatt_client.h new file mode 100644 index 00000000..b9bf9ce7 --- /dev/null +++ b/internal/platform/implementation/apple/ble_gatt_client.h @@ -0,0 +1,88 @@ +// 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. + +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data + +#import + +#include +#include + +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/uuid.h" + +@class GNCBLEGATTClient; + +namespace nearby { +namespace apple { + +class GattClient : public api::ble_v2::GattClient { + public: + explicit GattClient(GNCBLEGATTClient *gatt_client_); + ~GattClient() override = default; + + // Discovers the specified characteristics of a service. + // + // This method blocks until discovery has finished. + // + // Returns whether or not discovery finished successfully. + bool DiscoverServiceAndCharacteristics(const Uuid &service_uuid, + const std::vector &characteristic_uuids) override; + + // Retrieves a GATT characteristic. + // + // DiscoverServiceAndCharacteristics() must be called before this method to fetch all available + // services and characteristics first. + // + // On success, returns the characteristic. On error, returns nullptr. + std::optional GetCharacteristic( + const Uuid &service_uuid, const Uuid &characteristic_uuid) override; + + // Reads the requested characteristic from the associated remote device. + // + // On success, returns the characteristic's value. On error, returns nullptr. + std::optional ReadCharacteristic( + const api::ble_v2::GattCharacteristic &characteristic) override; + + // Writes a given characteristic and its values to the associated remote device. + // + // Returns whether or not the write was successful. + bool WriteCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, + absl::string_view value, + api::ble_v2::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_v2::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; + + private: + GNCBLEGATTClient *gatt_client_; +}; + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_gatt_client.mm b/internal/platform/implementation/apple/ble_gatt_client.mm new file mode 100644 index 00000000..13cbb684 --- /dev/null +++ b/internal/platform/implementation/apple/ble_gatt_client.mm @@ -0,0 +1,134 @@ +// 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/ble_gatt_client.h" + +#import +#import + +#include +#include +#include + +#include "internal/platform/implementation/ble_v2.h" + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h" +#import "internal/platform/implementation/apple/ble_utils.h" +#import "GoogleToolboxForMac/GTMLogger.h" + +namespace nearby { +namespace apple { + +GattClient::GattClient(GNCBLEGATTClient *gatt_client) : gatt_client_(gatt_client) {} + +bool GattClient::DiscoverServiceAndCharacteristics(const Uuid &service_uuid, + const std::vector &characteristic_uuids) { + CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); + NSMutableArray *characteristics = [[NSMutableArray alloc] init]; + for (const auto &uuid : characteristic_uuids) { + [characteristics addObject:CBUUID128FromCPP(uuid)]; + } + + NSCondition *condition = [[NSCondition alloc] init]; + [condition lock]; + __block NSError *blockError = nil; + [gatt_client_ discoverCharacteristicsWithUUIDs:characteristics + serviceUUID:serviceUUID + completionHandler:^(NSError *error) { + [condition lock]; + if (error != nil) { + GTMLoggerError(@"Error discovering characteristics: %@", error); + } + blockError = error; + [condition signal]; + [condition unlock]; + }]; + [condition wait]; + [condition unlock]; + return blockError == nil; +} + +std::optional GattClient::GetCharacteristic( + const Uuid &service_uuid, const Uuid &characteristic_uuid) { + CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); + CBUUID *characteristicUUID = CBUUID128FromCPP(characteristic_uuid); + + NSCondition *condition = [[NSCondition alloc] init]; + [condition lock]; + __block GNCBLEGATTCharacteristic *blockCharacteristic = nil; + __block NSError *blockError = nil; + [gatt_client_ characteristicWithUUID:characteristicUUID + serviceUUID:serviceUUID + completionHandler:^(GNCBLEGATTCharacteristic *characteristic, NSError *error) { + [condition lock]; + if (error != nil) { + GTMLoggerError(@"Error retrieving characteristic: %@", error); + } + blockCharacteristic = characteristic; + blockError = error; + [condition signal]; + [condition unlock]; + }]; + [condition wait]; + [condition unlock]; + if (blockCharacteristic == nil) { + return std::nullopt; + } + return CPPGATTCharacteristicFromObjC(blockCharacteristic); +} + +std::optional GattClient::ReadCharacteristic( + const api::ble_v2::GattCharacteristic &characteristic) { + NSCondition *condition = [[NSCondition alloc] init]; + [condition lock]; + __block NSData *blockValue = nil; + __block NSError *blockError = nil; + [gatt_client_ readValueForCharacteristic:ObjCGATTCharacteristicFromCPP(characteristic) + completionHandler:^(NSData *value, NSError *error) { + [condition lock]; + if (error != nil) { + GTMLoggerError(@"Error reading characteristic: %@", error); + } + blockValue = value; + blockError = error; + [condition signal]; + [condition unlock]; + }]; + [condition wait]; + [condition unlock]; + if (blockValue == nil) { + return std::nullopt; + } + return std::string((const char *)blockValue.bytes, blockValue.length); +} + +// TODO(b/290385712): Implement. +bool GattClient::WriteCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, + absl::string_view value, + api::ble_v2::GattClient::WriteType type) { + return false; +} + +// TODO(b/290385712): Implement. +bool GattClient::SetCharacteristicSubscription( + const api::ble_v2::GattCharacteristic &characteristic, bool enable, + absl::AnyInvocable on_characteristic_changed_cb) { + return false; +} + +// TODO(b/290385712): Implement. +void GattClient::Disconnect() {} + +} // namespace apple +} // namespace nearby From 7933842c2966c82fed0bb642af77919dc2f1bc81 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Thu, 17 Aug 2023 14:15:48 -0700 Subject: [PATCH 107/128] Add BLE Medium PiperOrigin-RevId: 557933813 --- .../apple/Mediums/BLEv2/GNCBLEError.h | 1 + .../apple/Mediums/BLEv2/GNCBLEGATTClient.h | 2 +- .../apple/Mediums/BLEv2/GNCBLEGATTClient.m | 2 +- .../apple/Mediums/BLEv2/GNCBLEGATTServer.m | 2 +- .../apple/Mediums/BLEv2/GNCBLEMedium.h | 139 +++++++++ .../apple/Mediums/BLEv2/GNCBLEMedium.m | 294 ++++++++++++++++++ .../apple/Mediums/BLEv2/GNCCentralManager.h | 181 +++++++++++ .../apple/Mediums/BLEv2/GNCCentralManager.m | 36 +++ .../apple/Mediums/BLEv2/GNCPeripheral.h | 10 + .../Mediums/BLEv2/NSData+GNCWebSafeBase64.h | 11 +- .../Mediums/BLEv2/NSData+GNCWebSafeBase64.m | 18 +- .../implementation/apple/Mediums/BUILD | 4 + .../platform/implementation/apple/Tests/BUILD | 15 +- .../apple/Tests/GNCBLEMedium+Testing.h | 42 +++ .../apple/Tests/GNCBLEMediumTest.m | 294 ++++++++++++++++++ .../apple/Tests/GNCFakeCentralManager.h | 70 +++++ .../apple/Tests/GNCFakeCentralManager.m | 86 +++++ .../apple/Tests/GNCFakePeripheral.m | 6 + .../apple/Tests/NSData+GNCWebSafeBase64Test.m | 54 +++- 19 files changed, 1250 insertions(+), 17 deletions(-) create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h create mode 100644 internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.m create mode 100644 internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h create mode 100644 internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m create mode 100644 internal/platform/implementation/apple/Tests/GNCFakeCentralManager.h create mode 100644 internal/platform/implementation/apple/Tests/GNCFakeCentralManager.m diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h index eb32fee7..0db6d168 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h @@ -26,4 +26,5 @@ typedef NS_ERROR_ENUM(GNCBLEErrorDomain, GNCBLEError){ GNCBLEErrorInvalidCharacteristic, GNCBLEErrorAlreadyDiscoveringSpecifiedCharacteristics, GNCBLEErrorAlreadyReadingCharacteristic, + GNCBLEErrorAlreadyScanning, }; diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h index c55017fc..f41f8f00 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h @@ -65,7 +65,7 @@ typedef void (^GNCReadCharacteristicValueCompletionHandler)(NSData *_Nullable va * * @param peripheral The peripheral instance. */ -- (instancetype)initWithPeripheral:(CBPeripheral *)peripheral; +- (instancetype)initWithPeripheral:(id)peripheral; /** * Discovers the specified characteristics of a service. diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m index 21871a87..8b91c879 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.m @@ -70,7 +70,7 @@ static NSError *AlreadyReadingCharacteristicError() { *_readCharacteristicValueCompletionHandlers; } -- (instancetype)initWithPeripheral:(CBPeripheral *)peripheral { +- (instancetype)initWithPeripheral:(id)peripheral { return [self initWithPeripheral:peripheral queue:dispatch_queue_create(kGNCBLEGATTClientQueueLabel, DISPATCH_QUEUE_SERIAL)]; diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m index 2cd60e63..1f996d69 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m @@ -203,7 +203,7 @@ static char *const kGNCBLEGATTServerQueueLabel = "com.nearby.GNCBLEGATTServer"; // data is unavailable. CBUUID *serviceUUID = [serviceData.allKeys objectAtIndex:0]; NSData *value = [serviceData objectForKey:serviceUUID]; - NSString *encoded = [value webSafebase64EncodedString]; + NSString *encoded = [value webSafeBase64EncodedString]; // Base64 encoding increases the size of the data so we must truncate it to 22 bytes to ensure // it fits in the advertisement alongside an assumed 16-bit serviceUUID. diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h new file mode 100644 index 00000000..d81b4975 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h @@ -0,0 +1,139 @@ +// 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 +#import + +@class GNCBLEGATTServer; +@class GNCBLEGATTClient; +@class GNCBLEGATTCharacteristic; + +@protocol GNCPeripheral; + +NS_ASSUME_NONNULL_BEGIN + +/** + * A block to be invoked when a call to @c startAdvertisingData:completionHandler: has completed. + * + * @param error The cause of the failure, or @c nil if no error occurred. + */ +typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error); + +/** + * A block to be invoked when a peripheral’s advertisement has been discovered. + * + * @note This block can be called numerous times. + * + * @param peripheral The discovered peripheral. + * @param serviceData A dictionary that contains service-specific advertisement data. The keys + * represent services and the values represent the service-specific data. + */ +typedef void (^GNCAdvertisementFoundHandler)(id peripheral, + NSDictionary *serviceData); + +/** + * A block to be invoked when a call to + * @c startScanningForService:advertisementFoundHandler:completionHandler: has completed. + * + * @param error The cause of the failure, or @c nil if no error occurred. + */ +typedef void (^GNCStartScanningCompletionHandler)(NSError *_Nullable error); + +/** + * A block to be invoked when a call to @c startGATTServerWithCompletionHandler: has completed. + * + * @param server The successfully started GATT server, or @c nil if an error occurred. + * @param error The cause of the failure, or @c nil if no error occurred. + */ +typedef void (^GNCGATTServerCompletionHandler)(GNCBLEGATTServer *_Nullable server, + NSError *_Nullable error); + +/** A block to be invoked when a peripheral has disconnected. */ +typedef void (^GNCGATTDisconnectionHandler)(); + +/** + * A block to be invoked when a call to + * @c connectToGATTServerForPeripheral:disconnectionHandler:completionHandler: has completed. + * + * @param client The interface to the remote peripheral’s GATT server, or @c nil if an error + * occurred. + * @param error The cause of the failure, or @c nil if no error occurred. + */ +typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable client, + NSError *_Nullable error); + +/** + * The main BLE medium used inside of Nearby. This serves as the entry point for all BLE and GATT + * related operations. + * + * @note The public APIs of this class are thread safe. + */ +@interface GNCBLEMedium : NSObject + +/** The hardware supports BOTH advertising extensions and extended scans. */ +@property(nonatomic, readonly) BOOL supportsExtendedAdvertisements; + +/** + * Starts advertising service data in a way that is supported by CoreBluetooth. + * + * Since CoreBluetooth doesn't support setting the @c CBAdvertisementDataServiceDataKey key, the + * service list is advertised using @c CBAdvertisementDataServiceUUIDsKey and the associated data is + * advertised using @c CBAdvertisementDataLocalNameKey. Since @c CBAdvertisementDataLocalNameKey + * does not support binary data, the value is base64 encoded and truncated if the resulting value is + * longer than 22 bytes. This also means we can only support advertising a single service. + * + * @param serviceData A dictionary that contains service-specific advertisement data. + * @param completionHandler Called on a private queue with @c nil if successfully started + * advertising or an error if one has occured. + */ +- (void)startAdvertisingData:(NSDictionary *)serviceData + completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler; + +/** + * Scans for peripherals that are advertising the specified service. + * + * @param serviceUUID The service UUID to scan for. + * @param advertisementFoundHandler Called on a private queue when a peripheral has been discovered. + * @param completionHandler Called on a private queue with @c nil if successfully started scanning + * or an error if one has occured. + */ +- (void)startScanningForService:(CBUUID *)serviceUUID + advertisementFoundHandler:(GNCAdvertisementFoundHandler)advertisementFoundHandler + completionHandler:(nullable GNCStartScanningCompletionHandler)completionHandler; + +/** + * Starts a GATT server. + * + * @param completionHandler Called on a private queue with the GATT server if successfully started + * or an error if one has occured. + */ +- (void)startGATTServerWithCompletionHandler: + (nullable GNCGATTServerCompletionHandler)completionHandler; + +/** + * Connects to a peripheral’s GATT server. + * + * @param remotePeripheral The peripheral to which the central is attempting to connect. + * @param disconnectionHandler Called on a private queue when the peripheral has been disconnected. + * @param completionHandler Called on a private queue with a GATT client if successfully connected + * or an error if one has occured. + */ +- (void)connectToGATTServerForPeripheral:(id)remotePeripheral + disconnectionHandler:(nullable GNCGATTDisconnectionHandler)disconnectionHandler + completionHandler: + (nullable GNCGATTConnectionCompletionHandler)completionHandler; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m new file mode 100644 index 00000000..7f1c0735 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m @@ -0,0 +1,294 @@ +// 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/BLEv2/GNCBLEMedium.h" + +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEError.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.h" + +NS_ASSUME_NONNULL_BEGIN + +static char *const kBLEMediumQueueLabel = "com.nearby.GNCBLEMedium"; + +static NSError *AlreadyScanningError() { + return [NSError errorWithDomain:GNCBLEErrorDomain code:GNCBLEErrorAlreadyScanning userInfo:nil]; +} + +@interface GNCBLEMedium () +@end + +@implementation GNCBLEMedium { + dispatch_queue_t _queue; + id _centralManager; + + // The active GATT server, or @nil if one hasn't been started yet. + GNCBLEGATTServer *_server; + + // The service that is being actively scanned for, or @c nil if not currently scanning. + CBUUID *_serviceUUID; + + // The handler called when an advertisement for the service represented by @c _serviceUUID has + // been discovered. This will be called continuously, until the peripheral disappears. + GNCAdvertisementFoundHandler _advertisementFoundHandler; + + // A peripheral to connection completion handler map. Used to track connection attempts. When a + // connection attempt has succeeded or failed, the completion handler is called and removed from + // the map. + NSMutableDictionary *_connectionCompletionHandlers; + + // A peripheral to disconnection handler map. Used to track when a peripheral becomes + // disconnected. Once disconnected, the completion handler is called and removed from the map. + NSMutableDictionary *_disconnectionHandlers; +} + +- (instancetype)init { + CBCentralManager *centralManager = + [[CBCentralManager alloc] initWithDelegate:self + queue:_queue + options:@{CBCentralManagerOptionShowPowerAlertKey : @NO}]; + dispatch_queue_t queue = dispatch_queue_create(kBLEMediumQueueLabel, DISPATCH_QUEUE_SERIAL); + return [self initWithCentralManager:centralManager 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 { + self = [super init]; + if (self) { + _queue = queue ?: dispatch_get_main_queue(); + _centralManager = centralManager; + _centralManager.centralDelegate = self; + _connectionCompletionHandlers = [NSMutableDictionary dictionary]; + _disconnectionHandlers = [NSMutableDictionary dictionary]; + } + return self; +} + +- (BOOL)supportsExtendedAdvertisements { + // TODO(b/294736083): CoreBluetooth doesn't support actually advertising any extensions, however + // some devices can scan for them if the feature is available. If we return @c YES from this + // method, we would be enabling advertising extensions (which won't work), so we must return @c NO + // until we add support for a new method to check only if extended scans are supported. + return NO; +} + +- (void)startAdvertisingData:(NSDictionary *)serviceData + completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + if (!_server) { + _server = [[GNCBLEGATTServer alloc] init]; + } + [_server startAdvertisingData:serviceData completionHandler:completionHandler]; + }); +} + +- (void)startScanningForService:(CBUUID *)serviceUUID + advertisementFoundHandler:(GNCAdvertisementFoundHandler)advertisementFoundHandler + completionHandler:(nullable GNCStartScanningCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + if (_serviceUUID) { + if (completionHandler) { + completionHandler(AlreadyScanningError()); + } + return; + } + + _serviceUUID = serviceUUID; + _advertisementFoundHandler = advertisementFoundHandler; + + [self internalStartScanningIfPoweredOn]; + if (completionHandler) { + completionHandler(nil); + } + }); +} + +- (void)startGATTServerWithCompletionHandler: + (nullable GNCGATTServerCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + if (!_server) { + _server = [[GNCBLEGATTServer alloc] init]; + } + if (completionHandler) { + completionHandler(_server, nil); + } + }); +} + +- (void)connectToGATTServerForPeripheral:(id)remotePeripheral + disconnectionHandler:(nullable GNCGATTDisconnectionHandler)disconnectionHandler + completionHandler: + (nullable GNCGATTConnectionCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + _disconnectionHandlers[remotePeripheral.identifier] = disconnectionHandler; + _connectionCompletionHandlers[remotePeripheral.identifier] = completionHandler; + [_centralManager connectPeripheral:remotePeripheral options:@{}]; + }); +} + +#pragma mark - Internal + +- (void)internalStartScanningIfPoweredOn { + 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 + // @c scanForPeripheralsWithServices:options: will be called anytime state transitions back to + // powered on. + if (_centralManager.state == CBManagerStatePoweredOn && _serviceUUID != nil) { + // Stop scanning just in case something outside of this class is already scanning. + [_centralManager stopScan]; + [_centralManager + scanForPeripheralsWithServices:@[ _serviceUUID ] + // Nearby relies on the existence of an advertisement for endpoint + // discovery/lost events, so we must set this key to keep the stream + // of duplicate delegate events flowing. This has adverse effect on + // battery life, but currently necessary. + options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES}]; + } +} + +- (NSDictionary *)decodeAdvertisementData: + (NSDictionary *)advertisementData { + dispatch_assert_queue(_queue); + // If service data is available, return it directly. + NSDictionary *serviceData = + advertisementData[CBAdvertisementDataServiceDataKey]; + if (serviceData) { + return serviceData; + } + + // Apple devices don't support advertising service data, so Apple devices advertise a base64 + // encoded local name, while other devices advertise service data. Here we attempt to reconstruct + // service data by decoding the local name. If successful, this is possibly a Nearby advertisement + // on an Apple device. + NSString *localName = advertisementData[CBAdvertisementDataLocalNameKey]; + if (!localName) { + return @{}; + } + NSData *data = [[NSData alloc] initWithWebSafeBase64EncodedString:localName]; + + // A Nearby Apple advertisement should only have a single service, so simply grab the first one if + // it exists. + NSArray *serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey]; + CBUUID *serviceUUID = serviceUUIDs.firstObject; + if (data && serviceUUID) { + return @{serviceUUID : data}; + } + + return @{}; +} + +#pragma mark - GNCCentralManagerDelegate + +- (void)gnc_centralManagerDidUpdateState:(id)central { + dispatch_assert_queue(_queue); + [self internalStartScanningIfPoweredOn]; +} + +- (void)gnc_centralManager:(id)central + didDiscoverPeripheral:(id)peripheral + advertisementData:(NSDictionary *)advertisementData + RSSI:(NSNumber *)RSSI { + dispatch_assert_queue(_queue); + if (_advertisementFoundHandler) { + _advertisementFoundHandler(peripheral, [self decodeAdvertisementData:advertisementData]); + } +} + +- (void)gnc_centralManager:(id)central + didConnectPeripheral:(id)peripheral { + dispatch_assert_queue(_queue); + GNCGATTConnectionCompletionHandler handler = _connectionCompletionHandlers[peripheral.identifier]; + _connectionCompletionHandlers[peripheral.identifier] = nil; + if (handler) { + GNCBLEGATTClient *client = [[GNCBLEGATTClient alloc] initWithPeripheral:peripheral]; + handler(client, nil); + } +} + +- (void)gnc_centralManager:(id)central + didFailToConnectPeripheral:(id)peripheral + error:(nullable NSError *)error { + dispatch_assert_queue(_queue); + GNCGATTConnectionCompletionHandler handler = _connectionCompletionHandlers[peripheral.identifier]; + _connectionCompletionHandlers[peripheral.identifier] = nil; + if (handler) { + handler(nil, error); + } +} + +- (void)gnc_centralManager:(id)central + didDisconnectPeripheral:(id)peripheral + error:(nullable NSError *)error { + dispatch_assert_queue(_queue); + GNCGATTDisconnectionHandler handler = _disconnectionHandlers[peripheral.identifier]; + _disconnectionHandlers[peripheral.identifier] = nil; + if (handler) { + handler(); + } +} + +#pragma mark - CBCentralManagerDelegate + +- (void)centralManagerDidUpdateState:(CBCentralManager *)central { + dispatch_async(_queue, ^{ + [self gnc_centralManagerDidUpdateState:central]; + }); +} + +- (void)centralManager:(CBCentralManager *)central + didDiscoverPeripheral:(CBPeripheral *)peripheral + advertisementData:(NSDictionary *)advertisementData + RSSI:(NSNumber *)RSSI { + dispatch_async(_queue, ^{ + [self gnc_centralManager:central + didDiscoverPeripheral:peripheral + advertisementData:advertisementData + RSSI:RSSI]; + }); +} + +- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { + dispatch_async(_queue, ^{ + [self gnc_centralManager:central didConnectPeripheral:peripheral]; + }); +} + +- (void)centralManager:(CBCentralManager *)central + didFailToConnectPeripheral:(CBPeripheral *)peripheral + error:(nullable NSError *)error { + dispatch_async(_queue, ^{ + [self gnc_centralManager:central didFailToConnectPeripheral:peripheral error:error]; + }); +} + +- (void)centralManager:(CBCentralManager *)central + didDisconnectPeripheral:(CBPeripheral *)peripheral + error:(nullable NSError *)error { + dispatch_async(_queue, ^{ + [self gnc_centralManager:central didDisconnectPeripheral:peripheral error:error]; + }); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h new file mode 100644 index 00000000..770377ea --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h @@ -0,0 +1,181 @@ +// 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 +#import + +@protocol GNCCentralManagerDelegate; +@protocol GNCPeripheral; + +NS_ASSUME_NONNULL_BEGIN + +/** Protocol which helps create a fake of a @c CBCentralManager to inject for testing. */ +@protocol GNCCentralManager + +/** Shadow property of a @c CBCentralManagerDelegate. */ +@property(weak, nonatomic, nullable) id centralDelegate; + +/** + * The current state of the manager. + * + * This state is initially set to @c CBManagerStateUnknown. When the state updates, the manager + * calls its delegate’s @c gnc_centralManagerDidUpdateState: method. + */ +@property(nonatomic, assign, readonly) CBManagerState state; + +/** + * Scans for peripherals that are advertising services. + * + * You can provide an array of @c CBUUID objects, representing service UUIDs, in the @c serviceUUIDs + * parameter. When you do, the central manager returns only peripherals that advertise the services + * you specify. If the @c serviceUUIDs parameter is @c nil, this method returns all discovered + * peripherals, regardless of their supported services. + * + * @note The recommended practice is to populate the @c serviceUUIDs parameter rather than leaving + * it @c nil. + * + * If the central manager is actively scanning with one set of parameters and it receives another + * set to scan, the new parameters override the previous set. When the central manager discovers a + * peripheral, it calls the @c gnc_centralManager:didDiscoverPeripheral:advertisementData:RSSI: + * method of its delegate object. + * + * Your app can scan for Bluetooth devices in the background by specifying the @c bluetooth-central + * background mode. To do this, your app must explicitly scan for one or more services by specifying + * them in the @c serviceUUIDs parameter. The CBCentralManager scan option has no effect while + * scanning in the background. + * + * @param serviceUUIDs An array of @c CBUUID objects that the app is interested in. Each @c CBUUID + * object represents the UUID of a service that a peripheral advertises. + * @param options A dictionary of options for customizing the scan. + */ +- (void)scanForPeripheralsWithServices:(nullable NSArray *)serviceUUIDs + options:(nullable NSDictionary *)options; + +/** + * Establishes a local connection to a peripheral. + * + * After successfully establishing a local connection to a peripheral, the central manager object + * calls the @c gnc_centralManager:didConnectPeripheral: method of its delegate object. If the + * connection attempt fails, the central manager object calls the + * @c gnc_centralManager:didFailToConnectPeripheral:error: method of its delegate object instead. + * Attempts to connect to a peripheral don’t time out. To explicitly cancel a pending connection to + * a peripheral, call the @c cancelPeripheralConnection: method. Deallocating @c peripheral also + * implicitly calls @c cancelPeripheralConnection:. + * + * @param peripheral The peripheral to which the central is attempting to connect. + * @param options A dictionary to customize the behavior of the connection. + */ +- (void)connectPeripheral:(id)peripheral + options:(nullable NSDictionary *)options; + +/** Asks the central manager to stop scanning for peripherals. */ +- (void)stopScan; + +@end + +/** + * Protocol which helps the @c GNCCentralManager wrap a @c CBCentralManagerDelegate for + * testing. + */ +@protocol GNCCentralManagerDelegate + +/** + * Tells the delegate the central manager’s state updated. + * + * You implement this required method to ensure that the central device supports Bluetooth low + * energy and that it’s available to use. You should issue commands to the central manager only when + * the central manager’s @c state indicates it’s powered on. A state with a value lower than + * @c CBManagerStatePoweredOn implies that scanning has stopped, which in turn disconnects any + * previously-connected peripherals. If the state moves below @c CBManagerStatePoweredOff, all + * @c CBPeripheral objects obtained from this central manager become invalid; you must retrieve or + * discover these peripherals again. + * + * @param central The central manager whose state has changed. + */ +- (void)gnc_centralManagerDidUpdateState:(id)central; + +/** + * Tells the delegate the central manager discovered a peripheral while scanning for devices. + * + * You must retain a local copy of the peripheral if you want to perform commands on it. Use the + * RSSI data to determine the proximity of a discoverable peripheral device, and whether you want to + * connect to it automatically. + * + * @param central The central manager that provides the update. + * @param peripheral The discovered peripheral. + * @param advertisementData A dictionary containing any advertisement data. + * @param RSSI The current received signal strength indicator (RSSI) of the peripheral, in decibels. + */ +- (void)gnc_centralManager:(id)central + didDiscoverPeripheral:(id)peripheral + advertisementData:(NSDictionary *)advertisementData + RSSI:(NSNumber *)RSSI; + +/** + * Tells the delegate that the central manager connected to a peripheral. + * + * The manager invokes this method when a call to @c connectPeripheral:options: succeeds. You + * typically implement this method to set the peripheral’s delegate and discover its services. + * + * @param central The central manager that provides this information. + * @param peripheral The now-connected peripheral. + */ +- (void)gnc_centralManager:(id)central + didConnectPeripheral:(id)peripheral; + +/** + * Tells the delegate the central manager failed to create a connection with a peripheral. + * + * The manager invokes this method when a connection initiated with the + * @c connectPeripheral:options: method fails to complete. Because connection attempts don’t time + * out, a failed connection usually indicates a transient issue, in which case you may attempt + * connecting to the peripheral again. + * + * @param central The central manager that provides this information. + * @param peripheral The peripheral that failed to connect. + * @param error The cause of the failure, or @c nil if no error occurred. + */ +- (void)gnc_centralManager:(id)central + didFailToConnectPeripheral:(id)peripheral + error:(nullable NSError *)error; + +/** + * Tells the delegate that the central manager disconnected from a peripheral. + * + * The manager invokes this method when disconnecting a peripheral previously connected with the + * @c connectPeripheral:options: method. The error parameter contains the reason for the + * disconnection, unless the disconnect resulted from a call to @c cancelPeripheralConnection:. + * + * All services, characteristics, and characteristic descriptors of a peripheral become invalidated + * after it disconnects. + * + * @param central The central manager that provides this information. + * @param peripheral The now-disconnected peripheral. + * @param error The cause of the failure, or @c nil if no error occurred. + */ +- (void)gnc_centralManager:(id)central + didDisconnectPeripheral:(id)peripheral + error:(nullable NSError *)error; + +@end + +/** + * Declares that @c CBCentralManager implements the @c GNCCentralManager protocol. + * + * This allows us to directly use a @c CBCentralManager as a @c GNCCentralManager. + */ +@interface CBCentralManager () +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.m new file mode 100644 index 00000000..921e8d92 --- /dev/null +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.m @@ -0,0 +1,36 @@ +// 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/BLEv2/GNCCentralManager.h" + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@implementation CBCentralManager (GNCCentralManagerAdditions) + +- (void)setCentralDelegate:(nullable id)centralDelegate { + NSAssert([centralDelegate conformsToProtocol:@protocol(CBCentralManagerDelegate)], + @"centralDelegate must conform to protocol CBCentralManagerDelegate"); + self.delegate = (id)centralDelegate; +} + +- (nullable id)centralDelegate { + return (id)self.delegate; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h index d3a7a1f8..c43b7b6b 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h @@ -43,6 +43,16 @@ NS_ASSUME_NONNULL_BEGIN */ @property(retain, readonly, nullable) NSArray *services; +/** + * The UUID associated with the peer. + * + * The value of this property represents the unique identifier of the peer. The first time a local + * manager encounters a peer, the system assigns the peer a UUID, represented by a new @c NSUUID + * object. Peers use @c NSUUID instances to identify themselves, instead of by the @c CBUUID objects + * that identify a peripheral’s services, characteristics, and descriptors. + */ +@property(readonly, nonatomic) NSUUID *identifier; + /** * Discovers the specified services of the peripheral. * diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.h b/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.h index c4d03b74..1143cef2 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.h @@ -19,7 +19,16 @@ NS_ASSUME_NONNULL_BEGIN @interface NSData (GNCWebSafeBase64) /** Creates a Base64 encoded string from the data using websafe characters and no padding. */ -- (NSString *)webSafebase64EncodedString; +- (NSString *)webSafeBase64EncodedString; + +/** + * Initializes a data object with the given Base64 encoded string. + * + * @param base64String A Base64 encoded string. + * @return A data object built by Base64 decoding the provided string. Returns @c nil if the data + * object could not be decoded. + */ +- (nullable instancetype)initWithWebSafeBase64EncodedString:(NSString *)base64String; @end diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.m b/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.m index 1f0b3014..e2dd69cb 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.m @@ -14,11 +14,13 @@ #import "internal/platform/implementation/apple/Mediums/BLEv2/NSData+GNCWebSafeBase64.h" +#import + NS_ASSUME_NONNULL_BEGIN @implementation NSData (GNCWebSafeBase64) -- (NSString *)webSafebase64EncodedString { +- (NSString *)webSafeBase64EncodedString { NSString *encoded = [self base64EncodedStringWithOptions:0]; // Convert the standard base64 characters to URL safe variants. @@ -29,6 +31,20 @@ NS_ASSUME_NONNULL_BEGIN return encoded; } +- (nullable instancetype)initWithWebSafeBase64EncodedString:(NSString *)base64String { + // Convert the URL safe base64 characters to the standard variants. + base64String = [base64String stringByReplacingOccurrencesOfString:@"-" withString:@"+"]; + base64String = [base64String stringByReplacingOccurrencesOfString:@"_" withString:@"/"]; + + // @c initWithBase64EncodedString:options: requires a padded base64 string. Append enough "=" + // characters to make the string a multiple of 4. + NSUInteger paddedLength = base64String.length + ((4 - (base64String.length % 4)) % 4); + base64String = [base64String stringByPaddingToLength:paddedLength + withString:@"=" + startingAtIndex:0]; + return [self initWithBase64EncodedString:base64String options:0]; +} + @end NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BUILD b/internal/platform/implementation/apple/Mediums/BUILD index aef72927..d9f5917d 100644 --- a/internal/platform/implementation/apple/Mediums/BUILD +++ b/internal/platform/implementation/apple/Mediums/BUILD @@ -22,6 +22,8 @@ objc_library( "BLEv2/GNCBLEGATTCharacteristic.m", "BLEv2/GNCBLEGATTClient.m", "BLEv2/GNCBLEGATTServer.m", + "BLEv2/GNCBLEMedium.m", + "BLEv2/GNCCentralManager.m", "BLEv2/GNCPeripheral.m", "BLEv2/GNCPeripheralManager.m", "BLEv2/NSData+GNCWebSafeBase64.m", @@ -44,6 +46,8 @@ objc_library( "BLEv2/GNCBLEGATTCharacteristic.h", "BLEv2/GNCBLEGATTClient.h", "BLEv2/GNCBLEGATTServer.h", + "BLEv2/GNCBLEMedium.h", + "BLEv2/GNCCentralManager.h", "BLEv2/GNCPeripheral.h", "BLEv2/GNCPeripheralManager.h", "BLEv2/NSData+GNCWebSafeBase64.h", diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index 864188a5..2ca87d00 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -24,13 +24,18 @@ objc_library( testonly = True, srcs = [ "GNCBLEGATTCharacteristicTest.mm", + "GNCBLEGATTClient+Testing.h", "GNCBLEGATTClientTest.m", "GNCBLEGATTServer+Testing.h", "GNCBLEGATTServerTest.m", + "GNCBLEMedium+Testing.h", + "GNCBLEMediumTest.m", "GNCBLEUtilsTest.mm", "GNCBleTest.mm", "GNCBluetoothAdapterTest.mm", "GNCCryptoTest.mm", + "GNCFakeCentralManager.h", + "GNCFakeCentralManager.m", "GNCFakePeripheral.h", "GNCFakePeripheral.m", "GNCFakePeripheralManager.h", @@ -43,7 +48,6 @@ objc_library( "NSData+GNCWebSafeBase64Test.m", ], deps = [ - ":GNCBLEGATTClient_Testing", "//internal/platform:base", "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", @@ -58,15 +62,6 @@ objc_library( ], ) -objc_library( - name = "GNCBLEGATTClient_Testing", - hdrs = ["GNCBLEGATTClient+Testing.h"], - deps = [ - "//internal/platform/implementation/apple/Mediums", - "//third_party/apple_frameworks:Foundation", - ], -) - ios_unit_test( name = "PlatformTests", minimum_os_version = IOS_MINIMUM_OS, diff --git a/internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h b/internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h new file mode 100644 index 00000000..3431613a --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h @@ -0,0 +1,42 @@ +// 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/BLEv2/GNCBLEMedium.h" + +#import + +@protocol GNCCentralManager; + +NS_ASSUME_NONNULL_BEGIN + +@interface GNCBLEMedium (Testing) + +/** + * Creates a BLE Medium with a provided central manager. + * + * This is only exposed for testing and can be used to inject a fake central manager. + * + * @param centralManager The central 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. + */ +- (instancetype)initWithCentralManager:(id)centralManager + queue:(nullable dispatch_queue_t)queue; + +- (NSDictionary *)decodeAdvertisementData: + (NSDictionary *)advertisementData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m b/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m new file mode 100644 index 00000000..e71f5fc5 --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m @@ -0,0 +1,294 @@ +// 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/BLEv2/GNCBLEMedium.h" + +#import +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" +#import "internal/platform/implementation/apple/Tests/GNCBLEMedium+Testing.h" +#import "internal/platform/implementation/apple/Tests/GNCFakeCentralManager.h" +#import "internal/platform/implementation/apple/Tests/GNCFakePeripheral.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]; +} + +#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]; +} + +#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 *connectExpectation = + [[XCTestExpectation alloc] initWithDescription:@"Connect."]; + 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); + [connectExpectation fulfill]; + }]; + + [self waitForExpectations:@[ connectExpectation ] timeout:3]; + + [fakeCentralManager simulateCentralManagerDidDisconnectPeripheral:peripheral]; + + [self waitForExpectations:@[ disconnectExpectation ] timeout:3]; +} + +@end diff --git a/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.h b/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.h new file mode 100644 index 00000000..2e2c15e5 --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.h @@ -0,0 +1,70 @@ +// 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 +#import + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h" + +NS_ASSUME_NONNULL_BEGIN + +/** A fake implementation of @c GNCCentralManager to inject for testing. */ +@interface GNCFakeCentralManager : NSObject + +/** The list of services being scanned for. */ +@property(nonatomic, nullable, readonly) NSArray *serviceUUIDs; + +/** + * Similates a @c connectPeripheral:options: error. + * + * Setting this error to a value other than @c nil will simulate a failure when calling + * @c connectPeripheral:options: and will call the + * @c gnc_centralManager:didFailToConnectPeripheral:error: delegate method with the provided error. + */ +@property(nonatomic, nullable, readwrite) NSError *didFailToConnectPeripheralError; + +/** + * Simulates a state update event. + * + * Updates the central manager state to the provided value and calls the + * @c gnc_centralManagerDidUpdateState: delegate method. + * + * @param fakeState The new state to transition to. + */ +- (void)simulateCentralManagerDidUpdateState:(CBManagerState)fakeState; + +/** + * Simulates a peripheral discovery event. + * + * Calls the @c gnc_centralManager:didDiscoverPeripheral:advertisementData:RSSI: delegate method. + * + * @param peripheral The discovered peripheral. + * @param peripheral A dictionary containing any advertisement data. + */ +- (void)simulateCentralManagerDidDiscoverPeripheral:(id)peripheral + advertisementData: + (NSDictionary *)advertisementData; + +/** + * Simulates a peripheral disconnection event. + * + * Calls the @c gnc_centralManager:didDisconnectPeripheral:error: delegate method. + * + * @param peripheral The now-disconnected peripheral. + */ +- (void)simulateCentralManagerDidDisconnectPeripheral:(id)peripheral; + +@end + +NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.m b/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.m new file mode 100644 index 00000000..dc080f7e --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCFakeCentralManager.m @@ -0,0 +1,86 @@ +// 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/Tests/GNCFakeCentralManager.h" + +#import +#import +#import + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCCentralManager.h" +#import "internal/platform/implementation/apple/Tests/GNCFakePeripheral.h" + +@implementation GNCFakeCentralManager { + CBManagerState _state; + NSArray *_serviceUUIDs; +} + +@synthesize centralDelegate; + +- (instancetype)init { + self = [super init]; + if (self) { + _state = CBManagerStateUnknown; + } + return self; +} + +- (CBManagerState)state { + return _state; +} + +- (void)scanForPeripheralsWithServices:(nullable NSArray *)serviceUUIDs + options:(nullable NSDictionary *)options { + _serviceUUIDs = serviceUUIDs; +} + +- (void)connectPeripheral:(id)peripheral + options:(nullable NSDictionary *)options { + if (_didFailToConnectPeripheralError) { + [centralDelegate gnc_centralManager:self + didFailToConnectPeripheral:peripheral + error:_didFailToConnectPeripheralError]; + return; + } + [centralDelegate gnc_centralManager:self didConnectPeripheral:peripheral]; +} + +- (void)stopScan { +} + +#pragma mark - Testing Helpers + +- (NSArray *)serviceUUIDs { + return _serviceUUIDs; +} + +- (void)simulateCentralManagerDidUpdateState:(CBManagerState)fakeState { + _state = fakeState; + [centralDelegate gnc_centralManagerDidUpdateState:self]; +} + +- (void)simulateCentralManagerDidDiscoverPeripheral:(id)peripheral + advertisementData: + (NSDictionary *)advertisementData { + [centralDelegate gnc_centralManager:self + didDiscoverPeripheral:peripheral + advertisementData:advertisementData + RSSI:[NSNumber numberWithInt:0]]; +} + +- (void)simulateCentralManagerDidDisconnectPeripheral:(id)peripheral { + [centralDelegate gnc_centralManager:self didDisconnectPeripheral:peripheral error:nil]; +} + +@end diff --git a/internal/platform/implementation/apple/Tests/GNCFakePeripheral.m b/internal/platform/implementation/apple/Tests/GNCFakePeripheral.m index 1fa97973..ebed3f24 100644 --- a/internal/platform/implementation/apple/Tests/GNCFakePeripheral.m +++ b/internal/platform/implementation/apple/Tests/GNCFakePeripheral.m @@ -38,6 +38,7 @@ NS_ASSUME_NONNULL_BEGIN @implementation GNCFakePeripheral { NSMutableArray *_services; + NSUUID *_identifier; } @synthesize peripheralDelegate; @@ -46,10 +47,15 @@ NS_ASSUME_NONNULL_BEGIN self = [super init]; if (self) { _services = [[NSMutableArray alloc] init]; + _identifier = [[NSUUID alloc] init]; } return self; } +- (NSUUID *)identifier { + return _identifier; +} + - (nullable NSArray *)services { return _services; } diff --git a/internal/platform/implementation/apple/Tests/NSData+GNCWebSafeBase64Test.m b/internal/platform/implementation/apple/Tests/NSData+GNCWebSafeBase64Test.m index ec1c14b6..8310962c 100644 --- a/internal/platform/implementation/apple/Tests/NSData+GNCWebSafeBase64Test.m +++ b/internal/platform/implementation/apple/Tests/NSData+GNCWebSafeBase64Test.m @@ -25,7 +25,7 @@ - (void)testEncodingWithPadding { NSString *expected = @"AQ"; NSData *data = [[NSData alloc] initWithBase64EncodedString:@"AQ==" options:0]; - NSString *actual = [data webSafebase64EncodedString]; + NSString *actual = [data webSafeBase64EncodedString]; XCTAssertEqualObjects(expected, actual); } @@ -35,8 +35,58 @@ [[NSData alloc] initWithBase64EncodedString: @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" options:0]; - NSString *actual = [data webSafebase64EncodedString]; + NSString *actual = [data webSafeBase64EncodedString]; XCTAssertEqualObjects(expected, actual); } +- (void)testDecodingWithoutPadding { + NSData *expected = [[NSData alloc] initWithBase64EncodedString:@"AQ==" options:0]; + NSData *actual = [[NSData alloc] initWithWebSafeBase64EncodedString:@"AQ"]; + XCTAssertNotNil(actual); + XCTAssertEqualObjects(expected, actual); +} + +- (void)testDecodingNoPad { + NSData *expected = [[NSData alloc] initWithBase64EncodedString:@"aaaa" options:0]; + NSData *actual = [[NSData alloc] initWithWebSafeBase64EncodedString:@"aaaa"]; + XCTAssertNotNil(actual); + XCTAssertEqualObjects(expected, actual); +} + +- (void)testDecoding1Pad { + NSData *expected = [[NSData alloc] initWithBase64EncodedString:@"aaa=" options:0]; + NSData *actual = [[NSData alloc] initWithWebSafeBase64EncodedString:@"aaa"]; + XCTAssertNotNil(actual); + XCTAssertEqualObjects(expected, actual); +} + +- (void)testDecoding2Pad { + NSData *expected = [[NSData alloc] initWithBase64EncodedString:@"aa==" options:0]; + NSData *actual = [[NSData alloc] initWithWebSafeBase64EncodedString:@"aa"]; + XCTAssertNotNil(actual); + XCTAssertEqualObjects(expected, actual); +} + +- (void)testDecodingSingleCharacterInLastQuadruple { + NSData *actual = [[NSData alloc] initWithWebSafeBase64EncodedString:@"aaaab"]; + XCTAssertNil(actual); +} + +- (void)testDecodingWithAllValidCharacters { + NSData *expected = + [[NSData alloc] initWithBase64EncodedString: + @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + options:0]; + NSData *actual = + [[NSData alloc] initWithWebSafeBase64EncodedString: + @"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"]; + XCTAssertNotNil(actual); + XCTAssertEqualObjects(expected, actual); +} + +- (void)testDecodingWithIllegalCharacters { + NSData *actual = [[NSData alloc] initWithWebSafeBase64EncodedString:@"@#$^&*()"]; + XCTAssertNil(actual); +} + @end From 408344244f4e4f86de7a9bae8f8055ffbd4b836a Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Thu, 17 Aug 2023 14:46:10 -0700 Subject: [PATCH 108/128] Set thread status PiperOrigin-RevId: 557942242 --- internal/platform/monitored_runnable.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/platform/monitored_runnable.cc b/internal/platform/monitored_runnable.cc index e0952298..792a580f 100644 --- a/internal/platform/monitored_runnable.cc +++ b/internal/platform/monitored_runnable.cc @@ -16,11 +16,13 @@ #include +// TODO: Support thread status #include "internal/platform/logging.h" #include "internal/platform/pending_job_registry.h" -namespace nearby { +#define SET_THREAD_STATUS(NAME) +namespace nearby { namespace { absl::Duration kMinReportedStartDelay = absl::Seconds(5); absl::Duration kMinReportedTaskDuration = absl::Seconds(10); @@ -36,6 +38,7 @@ MonitoredRunnable::MonitoredRunnable(const std::string& name, } void MonitoredRunnable::operator()() { + SET_THREAD_STATUS(name_.c_str()); auto start_time = SystemClock::ElapsedRealtime(); auto start_delay = start_time - post_time_; if (start_delay >= kMinReportedStartDelay) { From e38ce96964fe69a64fbb385d48f9467a81fbef19 Mon Sep 17 00:00:00 2001 From: Janusz Sobczak Date: Thu, 17 Aug 2023 15:13:00 -0700 Subject: [PATCH 109/128] Replace AcceptedConnectionCallback structs with absl::AnyInvocable PiperOrigin-RevId: 557949490 --- .../implementation/bluetooth_bwu_handler.cc | 8 +- .../implementation/mediums/ble_test.cc | 12 +-- connections/implementation/mediums/ble_v2.cc | 4 +- connections/implementation/mediums/ble_v2.h | 6 +- .../implementation/mediums/ble_v2_test.cc | 24 ++---- .../mediums/bluetooth_classic.cc | 5 +- .../mediums/bluetooth_classic.h | 10 +-- .../mediums/bluetooth_classic_test.cc | 50 ++++-------- .../implementation/mediums/webrtc_stub.h | 9 +-- .../implementation/mediums/wifi_direct.cc | 4 +- .../implementation/mediums/wifi_direct.h | 6 +- .../implementation/mediums/wifi_hotspot.cc | 6 +- .../implementation/mediums/wifi_hotspot.h | 6 +- .../implementation/mediums/wifi_lan.cc | 4 +- connections/implementation/mediums/wifi_lan.h | 6 +- .../implementation/mediums/wifi_lan_test.cc | 22 ++--- .../implementation/p2p_cluster_pcp_handler.cc | 81 +++++++++---------- .../implementation/wifi_direct_bwu_handler.cc | 8 +- .../wifi_hotspot_bwu_handler.cc | 8 +- .../implementation/wifi_lan_bwu_handler.cc | 7 +- internal/platform/ble.cc | 36 ++++----- internal/platform/ble.h | 6 +- internal/platform/ble_test.cc | 26 +++--- internal/platform/implementation/ble.h | 6 +- internal/platform/medium_environment.cc | 4 +- 25 files changed, 148 insertions(+), 216 deletions(-) diff --git a/connections/implementation/bluetooth_bwu_handler.cc b/connections/implementation/bluetooth_bwu_handler.cc index f60d607b..21328239 100644 --- a/connections/implementation/bluetooth_bwu_handler.cc +++ b/connections/implementation/bluetooth_bwu_handler.cc @@ -111,11 +111,9 @@ ByteArray BluetoothBwuHandler::HandleInitializeUpgradedMediumForEndpoint( if (!bluetooth_medium_.IsAcceptingConnections(upgrade_service_id)) { if (!bluetooth_medium_.StartAcceptingConnections( upgrade_service_id, - { - .accepted_cb = absl::bind_front( - &BluetoothBwuHandler::OnIncomingBluetoothConnection, this, - client), - })) { + absl::bind_front( + &BluetoothBwuHandler::OnIncomingBluetoothConnection, this, + client))) { NEARBY_LOGS(ERROR) << "BluetoothBwuHandler couldn't initiate the " "BLUETOOTH upgrade for endpoint " << endpoint_id diff --git a/connections/implementation/mediums/ble_test.cc b/connections/implementation/mediums/ble_test.cc index 9ec605b1..51673a77 100644 --- a/connections/implementation/mediums/ble_test.cc +++ b/connections/implementation/mediums/ble_test.cc @@ -72,11 +72,7 @@ TEST_P(BleTest, CanStartAcceptingConnectionsAndConnect) { fast_advertisement_service_uuid); ble_a.StartAcceptingConnections( service_id, - { - .accepted_cb = [&accept_latch]( - BleSocket socket, - const std::string&) { accept_latch.CountDown(); }, - }); + [&](BleSocket socket, const std::string&) { accept_latch.CountDown(); }); BlePeripheral discovered_peripheral; ble_b.StartScanning( service_id, fast_advertisement_service_uuid, @@ -126,11 +122,7 @@ TEST_P(BleTest, CanCancelConnect) { fast_advertisement_service_uuid); ble_a.StartAcceptingConnections( service_id, - { - .accepted_cb = [&accept_latch]( - BleSocket socket, - const std::string&) { accept_latch.CountDown(); }, - }); + [&](BleSocket socket, const std::string&) { accept_latch.CountDown(); }); BlePeripheral discovered_peripheral; ble_b.StartScanning( service_id, fast_advertisement_service_uuid, diff --git a/connections/implementation/mediums/ble_v2.cc b/connections/implementation/mediums/ble_v2.cc index cbdc5fc8..88131d85 100644 --- a/connections/implementation/mediums/ble_v2.cc +++ b/connections/implementation/mediums/ble_v2.cc @@ -417,7 +417,9 @@ bool BleV2::StartAcceptingConnections(const std::string& service_id, }); incoming_sockets_.insert({service_id, client_socket}); } - callback.accepted_cb(std::move(client_socket), service_id); + if (callback) { + callback(std::move(client_socket), service_id); + } } }); diff --git a/connections/implementation/mediums/ble_v2.h b/connections/implementation/mediums/ble_v2.h index 23ff3cf8..0040dbd0 100644 --- a/connections/implementation/mediums/ble_v2.h +++ b/connections/implementation/mediums/ble_v2.h @@ -48,10 +48,8 @@ class BleV2 final { using DiscoveredPeripheralCallback = mediums::DiscoveredPeripheralCallback; // Callback that is invoked when a new connection is accepted. - struct AcceptedConnectionCallback { - absl::AnyInvocable - accepted_cb = DefaultCallback(); - }; + using AcceptedConnectionCallback = absl::AnyInvocable; explicit BleV2(BluetoothRadio& bluetooth_radio); ~BleV2(); diff --git a/connections/implementation/mediums/ble_v2_test.cc b/connections/implementation/mediums/ble_v2_test.cc index 484d1471..d1d1ca5e 100644 --- a/connections/implementation/mediums/ble_v2_test.cc +++ b/connections/implementation/mediums/ble_v2_test.cc @@ -77,14 +77,10 @@ TEST_P(BleV2Test, CanConnect) { BleV2Socket socket_for_server; EXPECT_TRUE(ble_server.StartAcceptingConnections( - service_id, { - .accepted_cb = - [&socket_for_server, &accept_latch]( - BleV2Socket socket, const std::string&) { - socket_for_server = std::move(socket); - accept_latch.CountDown(); - }, - })); + service_id, [&](BleV2Socket socket, const std::string&) { + socket_for_server = std::move(socket); + accept_latch.CountDown(); + })); ble_server.StartAdvertising(service_id, advertisement_bytes, PowerLevel::kHighPower, @@ -139,14 +135,10 @@ TEST_P(BleV2Test, CanCancelConnect) { BleV2Socket socket_for_server; EXPECT_TRUE(ble_server.StartAcceptingConnections( - service_id, { - .accepted_cb = - [&socket_for_server, &accept_latch]( - BleV2Socket socket, const std::string&) { - socket_for_server = std::move(socket); - accept_latch.CountDown(); - }, - })); + service_id, [&](BleV2Socket socket, const std::string&) { + socket_for_server = std::move(socket); + accept_latch.CountDown(); + })); ble_server.StartAdvertising(service_id, advertisement_bytes, PowerLevel::kHighPower, diff --git a/connections/implementation/mediums/bluetooth_classic.cc b/connections/implementation/mediums/bluetooth_classic.cc index ccf42d62..34803fd8 100644 --- a/connections/implementation/mediums/bluetooth_classic.cc +++ b/connections/implementation/mediums/bluetooth_classic.cc @@ -298,8 +298,9 @@ bool BluetoothClassic::StartAcceptingConnections( server_socket.Close(); break; } - - callback.accepted_cb(service_id, std::move(client_socket)); + if (callback) { + callback(service_id, std::move(client_socket)); + } } }); diff --git a/connections/implementation/mediums/bluetooth_classic.h b/connections/implementation/mediums/bluetooth_classic.h index 415129ec..8c84215f 100644 --- a/connections/implementation/mediums/bluetooth_classic.h +++ b/connections/implementation/mediums/bluetooth_classic.h @@ -17,9 +17,9 @@ #include #include -#include -#include #include +#include +#include #include "absl/container/flat_hash_map.h" #include "connections/implementation/mediums/bluetooth_radio.h" @@ -40,10 +40,8 @@ class BluetoothClassic { using ScanMode = BluetoothAdapter::ScanMode; // Callback that is invoked when a new connection is accepted. - struct AcceptedConnectionCallback { - std::function - accepted_cb = [](const std::string&, BluetoothSocket) {}; - }; + using AcceptedConnectionCallback = absl::AnyInvocable; explicit BluetoothClassic(BluetoothRadio& radio); ~BluetoothClassic(); diff --git a/connections/implementation/mediums/bluetooth_classic_test.cc b/connections/implementation/mediums/bluetooth_classic_test.cc index 8a1e771e..0c618524 100644 --- a/connections/implementation/mediums/bluetooth_classic_test.cc +++ b/connections/implementation/mediums/bluetooth_classic_test.cc @@ -162,13 +162,9 @@ TEST_P(BluetoothClassicTest, CanConnect) { CountDownLatch accept_latch(1); EXPECT_TRUE(bt_server.StartAcceptingConnections( std::string(kServiceName1), - { - .accepted_cb = - [&socket_for_server, &accept_latch](const std::string& service_id, - BluetoothSocket socket) { - socket_for_server = std::move(socket); - accept_latch.CountDown(); - }, + [&](const std::string& service_id, BluetoothSocket socket) { + socket_for_server = std::move(socket); + accept_latch.CountDown(); })); CancellationFlag flag; BluetoothSocket socket_for_client = @@ -217,13 +213,9 @@ TEST_P(BluetoothClassicTest, CanCancelBeforeConnect) { CountDownLatch accept_latch(1); EXPECT_TRUE(bt_server.StartAcceptingConnections( std::string(kServiceName1), - { - .accepted_cb = - [&socket_for_server, &accept_latch](const std::string& service_id, - BluetoothSocket socket) { - socket_for_server = std::move(socket); - accept_latch.CountDown(); - }, + [&](const std::string& service_id, BluetoothSocket socket) { + socket_for_server = std::move(socket); + accept_latch.CountDown(); })); CancellationFlag flag(true); BluetoothSocket socket_for_client = @@ -288,13 +280,9 @@ TEST_P(BluetoothClassicTest, CanCancelDuringConnect) { CountDownLatch accept_latch(1); EXPECT_TRUE(bt_server.StartAcceptingConnections( std::string(kServiceName1), - { - .accepted_cb = - [&socket_for_server, &accept_latch](const std::string& service_id, - BluetoothSocket socket) { - socket_for_server = std::move(socket); - accept_latch.CountDown(); - }, + [&](const std::string& service_id, BluetoothSocket socket) { + socket_for_server = std::move(socket); + accept_latch.CountDown(); })); CancellationFlag flag; BluetoothSocket socket_for_client = @@ -361,13 +349,9 @@ TEST_P(BluetoothClassicTest, CanCancelDuringConnect_MultipleEndpoints) { EXPECT_TRUE(bt_server.StartAcceptingConnections( std::string(kServiceName1), - { - .accepted_cb = - [&socket_for_server1, &accept_latch]( - const std::string& service_id, BluetoothSocket socket) { - socket_for_server1 = std::move(socket); - accept_latch.CountDown(); - }, + [&](const std::string& service_id, BluetoothSocket socket) { + socket_for_server1 = std::move(socket); + accept_latch.CountDown(); })); CancellationFlag flag; BluetoothSocket socket_for_client1 = @@ -378,13 +362,9 @@ TEST_P(BluetoothClassicTest, CanCancelDuringConnect_MultipleEndpoints) { medium_a_->CancelDuringConnectToService(); EXPECT_TRUE(bt_server.StartAcceptingConnections( std::string(kServiceName2), - { - .accepted_cb = - [&socket_for_server2, &accept_latch]( - const std::string& service_id, BluetoothSocket socket) { - socket_for_server2 = std::move(socket); - accept_latch.CountDown(); - }, + [&](const std::string& service_id, BluetoothSocket socket) { + socket_for_server2 = std::move(socket); + accept_latch.CountDown(); })); BluetoothSocket socket_for_client2 = diff --git a/connections/implementation/mediums/webrtc_stub.h b/connections/implementation/mediums/webrtc_stub.h index a0609d95..3bc01fe7 100644 --- a/connections/implementation/mediums/webrtc_stub.h +++ b/connections/implementation/mediums/webrtc_stub.h @@ -31,14 +31,13 @@ namespace nearby { namespace connections { namespace mediums { -// Callback that is invoked when a new connection is accepted. -struct AcceptedConnectionCallback { - std::function accepted_cb = - [](WebRtcSocketWrapper) {}; -}; + // 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(); diff --git a/connections/implementation/mediums/wifi_direct.cc b/connections/implementation/mediums/wifi_direct.cc index aa03650d..81020856 100644 --- a/connections/implementation/mediums/wifi_direct.cc +++ b/connections/implementation/mediums/wifi_direct.cc @@ -185,7 +185,9 @@ bool WifiDirect::StartAcceptingConnections( server_socket.Close(); break; } - callback.accepted_cb(service_id, std::move(client_socket)); + if (callback) { + callback(service_id, std::move(client_socket)); + } } }); diff --git a/connections/implementation/mediums/wifi_direct.h b/connections/implementation/mediums/wifi_direct.h index f4ef22af..899b7d71 100644 --- a/connections/implementation/mediums/wifi_direct.h +++ b/connections/implementation/mediums/wifi_direct.h @@ -29,10 +29,8 @@ namespace connections { class WifiDirect { public: // Callback that is invoked when a new connection is accepted. - struct AcceptedConnectionCallback { - std::function - accepted_cb = [](const std::string&, WifiDirectSocket) {}; - }; + using AcceptedConnectionCallback = absl::AnyInvocable; WifiDirect() : is_go_started_(false), is_connected_to_go_(false) {} ~WifiDirect(); diff --git a/connections/implementation/mediums/wifi_hotspot.cc b/connections/implementation/mediums/wifi_hotspot.cc index 27b8256e..8605ad1d 100644 --- a/connections/implementation/mediums/wifi_hotspot.cc +++ b/connections/implementation/mediums/wifi_hotspot.cc @@ -14,8 +14,8 @@ #include "connections/implementation/mediums/wifi_hotspot.h" -#include #include +#include #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" @@ -188,7 +188,9 @@ bool WifiHotspot::StartAcceptingConnections( server_socket.Close(); break; } - callback.accepted_cb(service_id, std::move(client_socket)); + if (callback) { + callback(service_id, std::move(client_socket)); + } } }); diff --git a/connections/implementation/mediums/wifi_hotspot.h b/connections/implementation/mediums/wifi_hotspot.h index 7ca8e210..6b63eac7 100644 --- a/connections/implementation/mediums/wifi_hotspot.h +++ b/connections/implementation/mediums/wifi_hotspot.h @@ -29,10 +29,8 @@ namespace connections { class WifiHotspot { public: // Callback that is invoked when a new connection is accepted. - struct AcceptedConnectionCallback { - std::function - accepted_cb = [](const std::string&, WifiHotspotSocket) {}; - }; + using AcceptedConnectionCallback = absl::AnyInvocable; WifiHotspot() : is_hotspot_started_(false), is_connected_to_hotspot_(false) {} ~WifiHotspot(); diff --git a/connections/implementation/mediums/wifi_lan.cc b/connections/implementation/mediums/wifi_lan.cc index 44b18caa..2c3e262e 100644 --- a/connections/implementation/mediums/wifi_lan.cc +++ b/connections/implementation/mediums/wifi_lan.cc @@ -264,7 +264,9 @@ bool WifiLan::StartAcceptingConnections(const std::string& service_id, server_socket.Close(); break; } - callback.accepted_cb(service_id, std::move(client_socket)); + if (callback) { + callback(service_id, std::move(client_socket)); + } } }); diff --git a/connections/implementation/mediums/wifi_lan.h b/connections/implementation/mediums/wifi_lan.h index 9c0dd271..b9d53f22 100644 --- a/connections/implementation/mediums/wifi_lan.h +++ b/connections/implementation/mediums/wifi_lan.h @@ -36,10 +36,8 @@ class WifiLan { using DiscoveredServiceCallback = WifiLanMedium::DiscoveredServiceCallback; // Callback that is invoked when a new connection is accepted. - struct AcceptedConnectionCallback { - std::function - accepted_cb = [](const std::string&, WifiLanSocket) {}; - }; + using AcceptedConnectionCallback = absl::AnyInvocable; WifiLan() = default; ~WifiLan(); diff --git a/connections/implementation/mediums/wifi_lan_test.cc b/connections/implementation/mediums/wifi_lan_test.cc index fd23f51a..acd1df81 100644 --- a/connections/implementation/mediums/wifi_lan_test.cc +++ b/connections/implementation/mediums/wifi_lan_test.cc @@ -68,14 +68,9 @@ TEST_P(WifiLanTest, CanConnect) { WifiLanSocket socket_for_server; EXPECT_TRUE(wifi_lan_server.StartAcceptingConnections( - service_id, - { - .accepted_cb = - [&socket_for_server, &accept_latch](const std::string& service_id, - WifiLanSocket socket) { - socket_for_server = std::move(socket); - accept_latch.CountDown(); - }, + service_id, [&](const std::string& service_id, WifiLanSocket socket) { + socket_for_server = std::move(socket); + accept_latch.CountDown(); })); NsdServiceInfo nsd_service_info; @@ -125,14 +120,9 @@ TEST_P(WifiLanTest, CanCancelConnect) { WifiLanSocket socket_for_server; EXPECT_TRUE(wifi_lan_server.StartAcceptingConnections( - service_id, - { - .accepted_cb = - [&socket_for_server, &accept_latch](const std::string& service_id, - WifiLanSocket socket) { - socket_for_server = std::move(socket); - accept_latch.CountDown(); - }, + service_id, [&](const std::string& service_id, WifiLanSocket socket) { + socket_for_server = std::move(socket); + accept_latch.CountDown(); })); NsdServiceInfo nsd_service_info; diff --git a/connections/implementation/p2p_cluster_pcp_handler.cc b/connections/implementation/p2p_cluster_pcp_handler.cc index ab6491f8..da91affa 100644 --- a/connections/implementation/p2p_cluster_pcp_handler.cc +++ b/connections/implementation/p2p_cluster_pcp_handler.cc @@ -1087,10 +1087,10 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl( !bluetooth_medium_.IsAcceptingConnections(std::string(service_id))) { if (!bluetooth_medium_.StartAcceptingConnections( std::string(service_id), - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, - this, client_proxy, local_endpoint_id, - options.listening_endpoint_type)})) { + absl::bind_front( + &P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, this, + client_proxy, local_endpoint_id, + options.listening_endpoint_type))) { NEARBY_LOGS(WARNING) << "Failed to start listening for incoming connections on Bluetooth"; } else { @@ -1106,10 +1106,10 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl( !ble_v2_medium_.IsAcceptingConnections(std::string(service_id))) { if (!ble_v2_medium_.StartAcceptingConnections( std::string(service_id), - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::BleV2ConnectionAcceptedHandler, this, - client_proxy, local_endpoint_id, - options.listening_endpoint_type)})) { + absl::bind_front( + &P2pClusterPcpHandler::BleV2ConnectionAcceptedHandler, this, + client_proxy, local_endpoint_id, + options.listening_endpoint_type))) { NEARBY_LOGS(WARNING) << "Failed to start listening for incoming connections on ble_v2"; } else { @@ -1122,10 +1122,10 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl( !ble_medium_.IsAcceptingConnections(std::string(service_id))) { if (!ble_medium_.StartAcceptingConnections( std::string(service_id), - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::BleConnectionAcceptedHandler, this, - client_proxy, local_endpoint_id, - options.listening_endpoint_type)})) { + absl::bind_front( + &P2pClusterPcpHandler::BleConnectionAcceptedHandler, this, + client_proxy, local_endpoint_id, + options.listening_endpoint_type))) { NEARBY_LOGS(WARNING) << "Failed to start listening for incoming connections on ble"; } else { @@ -1137,10 +1137,10 @@ P2pClusterPcpHandler::StartListeningForIncomingConnectionsImpl( !wifi_lan_medium_.IsAcceptingConnections(std::string(service_id))) { if (!wifi_lan_medium_.StartAcceptingConnections( std::string(service_id), - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::WifiLanConnectionAcceptedHandler, this, - client_proxy, local_endpoint_id, "", - options.listening_endpoint_type)})) { + absl::bind_front( + &P2pClusterPcpHandler::WifiLanConnectionAcceptedHandler, this, + client_proxy, local_endpoint_id, "", + options.listening_endpoint_type))) { NEARBY_LOGS(WARNING) << "Failed to start listening for incoming connections on wifi_lan"; } else { @@ -1458,10 +1458,10 @@ Medium P2pClusterPcpHandler::StartBluetoothAdvertising( if (!bluetooth_radio_.Enable() || !bluetooth_medium_.StartAcceptingConnections( service_id, - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, - this, client, local_endpoint_info.AsStringView(), - NearbyDevice::Type::kConnectionsDevice)})) { + absl::bind_front( + &P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, this, + client, local_endpoint_info.AsStringView(), + NearbyDevice::Type::kConnectionsDevice))) { NEARBY_LOGS(WARNING) << "In StartBluetoothAdvertising(" << absl::BytesToHexString(local_endpoint_info.data()) @@ -1635,11 +1635,10 @@ Medium P2pClusterPcpHandler::StartBleAdvertising( if (!ble_medium_.IsAcceptingConnections(service_id)) { if (!bluetooth_radio_.Enable() || !ble_medium_.StartAcceptingConnections( - service_id, - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::BleConnectionAcceptedHandler, this, - client, local_endpoint_info.AsStringView(), - NearbyDevice::Type::kConnectionsDevice)})) { + service_id, absl::bind_front( + &P2pClusterPcpHandler::BleConnectionAcceptedHandler, + this, client, local_endpoint_info.AsStringView(), + NearbyDevice::Type::kConnectionsDevice))) { NEARBY_LOGS(WARNING) << "In StartBleAdvertising(" << absl::BytesToHexString(local_endpoint_info.data()) @@ -1664,10 +1663,10 @@ Medium P2pClusterPcpHandler::StartBleAdvertising( if (!bluetooth_radio_.Enable() || !bluetooth_medium_.StartAcceptingConnections( service_id, - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, - this, client, local_endpoint_info.AsStringView(), - NearbyDevice::Type::kConnectionsDevice)})) { + absl::bind_front( + &P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, + this, client, local_endpoint_info.AsStringView(), + NearbyDevice::Type::kConnectionsDevice))) { NEARBY_LOGS(WARNING) << "In BT StartBleAdvertising(" << absl::BytesToHexString(local_endpoint_info.data()) @@ -1845,10 +1844,10 @@ Medium P2pClusterPcpHandler::StartBleV2Advertising( if (!bluetooth_radio_.Enable() || !ble_v2_medium_.StartAcceptingConnections( service_id, - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::BleV2ConnectionAcceptedHandler, this, - client, local_endpoint_info.AsStringView(), - NearbyDevice::Type::kConnectionsDevice)})) { + absl::bind_front( + &P2pClusterPcpHandler::BleV2ConnectionAcceptedHandler, this, + client, local_endpoint_info.AsStringView(), + NearbyDevice::Type::kConnectionsDevice))) { NEARBY_LOGS(WARNING) << "In StartBleAdvertising(" << absl::BytesToHexString(local_endpoint_info.data()) @@ -1876,10 +1875,10 @@ Medium P2pClusterPcpHandler::StartBleV2Advertising( if (!bluetooth_radio_.Enable() || !bluetooth_medium_.StartAcceptingConnections( service_id, - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, - this, client, local_endpoint_info.AsStringView(), - NearbyDevice::Type::kConnectionsDevice)})) { + absl::bind_front( + &P2pClusterPcpHandler::BluetoothConnectionAcceptedHandler, + this, client, local_endpoint_info.AsStringView(), + NearbyDevice::Type::kConnectionsDevice))) { NEARBY_LOGS(WARNING) << "In BT StartBleAdvertising(" << absl::BytesToHexString(local_endpoint_info.data()) @@ -2057,10 +2056,10 @@ Medium P2pClusterPcpHandler::StartWifiLanAdvertising( if (!wifi_lan_medium_.IsAcceptingConnections(service_id)) { if (!wifi_lan_medium_.StartAcceptingConnections( service_id, - {.accepted_cb = absl::bind_front( - &P2pClusterPcpHandler::WifiLanConnectionAcceptedHandler, this, - client, local_endpoint_id, local_endpoint_info.AsStringView(), - NearbyDevice::Type::kConnectionsDevice)})) { + absl::bind_front( + &P2pClusterPcpHandler::WifiLanConnectionAcceptedHandler, this, + client, local_endpoint_id, local_endpoint_info.AsStringView(), + NearbyDevice::Type::kConnectionsDevice))) { NEARBY_LOGS(WARNING) << "In StartWifiLanAdvertising(" << absl::BytesToHexString(local_endpoint_info.data()) diff --git a/connections/implementation/wifi_direct_bwu_handler.cc b/connections/implementation/wifi_direct_bwu_handler.cc index daa5d04b..0ff387c1 100644 --- a/connections/implementation/wifi_direct_bwu_handler.cc +++ b/connections/implementation/wifi_direct_bwu_handler.cc @@ -45,11 +45,9 @@ ByteArray WifiDirectBwuHandler::HandleInitializeUpgradedMediumForEndpoint( if (!wifi_direct_medium_.IsAcceptingConnections(upgrade_service_id)) { if (!wifi_direct_medium_.StartAcceptingConnections( upgrade_service_id, - { - .accepted_cb = absl::bind_front( - &WifiDirectBwuHandler::OnIncomingWifiDirectConnection, this, - client), - })) { + absl::bind_front( + &WifiDirectBwuHandler::OnIncomingWifiDirectConnection, this, + client))) { NEARBY_LOGS(ERROR) << "WifiDirectBwuHandler couldn't initiate WifiDirect upgrade for " << "service " << upgrade_service_id << " and endpoint " << endpoint_id diff --git a/connections/implementation/wifi_hotspot_bwu_handler.cc b/connections/implementation/wifi_hotspot_bwu_handler.cc index 4bf7f215..9479924d 100644 --- a/connections/implementation/wifi_hotspot_bwu_handler.cc +++ b/connections/implementation/wifi_hotspot_bwu_handler.cc @@ -49,11 +49,9 @@ ByteArray WifiHotspotBwuHandler::HandleInitializeUpgradedMediumForEndpoint( if (!wifi_hotspot_medium_.IsAcceptingConnections(upgrade_service_id)) { if (!wifi_hotspot_medium_.StartAcceptingConnections( upgrade_service_id, - { - .accepted_cb = absl::bind_front( - &WifiHotspotBwuHandler::OnIncomingWifiHotspotConnection, - this, client), - })) { + absl::bind_front( + &WifiHotspotBwuHandler::OnIncomingWifiHotspotConnection, this, + client))) { NEARBY_LOGS(ERROR) << "WifiHotspotBwuHandler couldn't initiate WifiHotspot upgrade for " << "service " << upgrade_service_id << " and endpoint " << endpoint_id diff --git a/connections/implementation/wifi_lan_bwu_handler.cc b/connections/implementation/wifi_lan_bwu_handler.cc index 04a0ea50..2a455e0c 100644 --- a/connections/implementation/wifi_lan_bwu_handler.cc +++ b/connections/implementation/wifi_lan_bwu_handler.cc @@ -95,11 +95,8 @@ ByteArray WifiLanBwuHandler::HandleInitializeUpgradedMediumForEndpoint( if (!wifi_lan_medium_.IsAcceptingConnections(upgrade_service_id)) { if (!wifi_lan_medium_.StartAcceptingConnections( upgrade_service_id, - { - .accepted_cb = absl::bind_front( - &WifiLanBwuHandler::OnIncomingWifiLanConnection, this, - client), - })) { + absl::bind_front(&WifiLanBwuHandler::OnIncomingWifiLanConnection, + this, client))) { NEARBY_LOGS(ERROR) << "WifiLanBwuHandler couldn't initiate the WifiLan upgrade for " << "service " << upgrade_service_id << " and endpoint " << endpoint_id diff --git a/internal/platform/ble.cc b/internal/platform/ble.cc index a47f1852..d6792c22 100644 --- a/internal/platform/ble.cc +++ b/internal/platform/ble.cc @@ -90,31 +90,29 @@ bool BleMedium::StartAcceptingConnections(const std::string& service_id, } return impl_->StartAcceptingConnections( service_id, - { - .accepted_cb = - [this](api::BleSocket& socket, const std::string& service_id) { - MutexLock lock(&mutex_); - auto pair = sockets_.emplace( - &socket, absl::make_unique()); - auto& context = *pair.first->second; - if (!pair.second) { - NEARBY_LOG(INFO, "Accepting (again) socket=%p, impl=%p", - &context.socket, &socket); - } else { - context.socket = BleSocket(&socket); - NEARBY_LOG(INFO, "Accepting socket=%p, impl=%p", - &context.socket, &socket); - } - accepted_connection_callback_.accepted_cb(context.socket, - service_id); - }, + [this](api::BleSocket& socket, const std::string& service_id) { + MutexLock lock(&mutex_); + auto pair = sockets_.emplace( + &socket, std::make_unique()); + auto& context = *pair.first->second; + if (!pair.second) { + NEARBY_LOG(INFO, "Accepting (again) socket=%p, impl=%p", + &context.socket, &socket); + } else { + context.socket = BleSocket(&socket); + NEARBY_LOG(INFO, "Accepting socket=%p, impl=%p", &context.socket, + &socket); + } + if (accepted_connection_callback_) { + accepted_connection_callback_(context.socket, service_id); + } }); } bool BleMedium::StopAcceptingConnections(const std::string& service_id) { { MutexLock lock(&mutex_); - accepted_connection_callback_ = {}; + accepted_connection_callback_ = nullptr; sockets_.clear(); NEARBY_LOG(INFO, "Ble accepted connection disabled: impl=%p", &GetImpl()); } diff --git a/internal/platform/ble.h b/internal/platform/ble.h index df28b38f..cf1f4fab 100644 --- a/internal/platform/ble.h +++ b/internal/platform/ble.h @@ -100,10 +100,8 @@ class BleMedium final { BlePeripheral peripheral; }; - struct AcceptedConnectionCallback { - absl::AnyInvocable - accepted_cb = DefaultCallback(); - }; + using AcceptedConnectionCallback = absl::AnyInvocable; struct AcceptedConnectionInfo { BleSocket socket; }; diff --git a/internal/platform/ble_test.cc b/internal/platform/ble_test.cc index d526e3df..8f48b908 100644 --- a/internal/platform/ble_test.cc +++ b/internal/platform/ble_test.cc @@ -86,14 +86,11 @@ TEST_P(BleMediumTest, CanStartAcceptingConnectionsAndConnect) { ble_b.StartAdvertising(service_id, advertisement_bytes, fast_advertisement_service_uuid); ble_b.StartAcceptingConnections( - service_id, - AcceptedConnectionCallback{ - .accepted_cb = [&accepted_latch](BleSocket socket, - const std::string& service_id) { - NEARBY_LOG(INFO, "Connection accepted: socket=%p, service_id=%s", - &socket, service_id.c_str()); - accepted_latch.CountDown(); - }}); + service_id, [&](BleSocket socket, const std::string& service_id) { + NEARBY_LOG(INFO, "Connection accepted: socket=%p, service_id=%s", + &socket, service_id.c_str()); + accepted_latch.CountDown(); + }); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); BleSocket socket_a; @@ -148,14 +145,11 @@ TEST_P(BleMediumTest, CanCancelConnect) { ble_b.StartAdvertising(service_id, advertisement_bytes, fast_advertisement_service_uuid); ble_b.StartAcceptingConnections( - service_id, - AcceptedConnectionCallback{ - .accepted_cb = [&accepted_latch](BleSocket socket, - const std::string& service_id) { - NEARBY_LOG(INFO, "Connection accepted: socket=%p, service_id=%s", - &socket, service_id.c_str()); - accepted_latch.CountDown(); - }}); + service_id, [&](BleSocket socket, const std::string& service_id) { + NEARBY_LOG(INFO, "Connection accepted: socket=%p, service_id=%s", + &socket, service_id.c_str()); + accepted_latch.CountDown(); + }); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); BleSocket socket_a; diff --git a/internal/platform/implementation/ble.h b/internal/platform/implementation/ble.h index c97b17de..b8be0f52 100644 --- a/internal/platform/implementation/ble.h +++ b/internal/platform/implementation/ble.h @@ -99,10 +99,8 @@ class BleMedium { virtual bool StopScanning(const std::string& service_id) = 0; // Callback that is invoked when a new connection is accepted. - struct AcceptedConnectionCallback { - absl::AnyInvocable - accepted_cb = DefaultCallback(); - }; + using AcceptedConnectionCallback = absl::AnyInvocable; // Returns true once BLE socket connection requests to service_id can be // accepted. diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index a5831ccc..8ed0d104 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -576,7 +576,9 @@ void MediumEnvironment::CallBleAcceptedConnectionCallback( return; } auto& info = item->second; - info.accepted_connection_callback.accepted_cb(socket, service_id); + if (info.accepted_connection_callback) { + info.accepted_connection_callback(socket, service_id); + } }); } From 71c298f2702f67bb25e1cd899b66f1e5e77cd1b9 Mon Sep 17 00:00:00 2001 From: hai007 Date: Fri, 18 Aug 2023 02:09:59 -0700 Subject: [PATCH 110/128] [Nearby Sharing] log connection lost for connection/transfer phase PiperOrigin-RevId: 558083193 --- proto/sharing_enums.proto | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index bd99d73b..196adf2f 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -288,6 +288,7 @@ enum EstablishConnectionStatus { CONNECTION_STATUS_FAILED_WRITE_INTRODUCTION = 6; CONNECTION_STATUS_FAILED_NULL_CONNECTION = 7; CONNECTION_STATUS_FAILED_NO_TRANSFER_UPDATE_CALLBACK = 8; + CONNECTION_STATUS_LOST_CONNECTIVITY = 9; } // The status of sending and receiving attachments. Used by SEND_ATTACHMENTS. @@ -333,10 +334,13 @@ enum AttachmentTransmissionStatus { FAILED_UNKNOWN_REMOTE_RESPONSE_TRANSMISSION_STATUS = 26; // Connection failed due to Wifi is disconnected or Bluetooth setting is off // or user turn on airplane mode. - NO_RESPONSE_FRAME_CONNECTION_CLOSED_LOST_CONNECTIVITY_TRANSMISSION_STATUS = - 27; + NO_RESPONSE_FRAME_CONNECTION_CLOSED_LOST_CONNECTIVITY_TRANSMISSION_STATUS = 27 + [deprecated = true]; // Unexpected connection failure due to no response frame. NO_RESPONSE_FRAME_CONNECTION_CLOSED_TRANSMISSION_STATUS = 28; + // Connection failed due to Wifi is disconnected or Bluetooth setting is off + // or user turn on airplane mode. + LOST_CONNECTIVITY_TRANSMISSION_STATUS = 29; } // Generic result status of NearbyConnections API calls. From 9998093366f2159e502286cdf6e6d1530270d6e4 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Fri, 18 Aug 2023 16:35:50 -0700 Subject: [PATCH 111/128] Add Objective-C++ BLE Medium wrapper PiperOrigin-RevId: 558278732 --- Package.swift | 1 + internal/platform/implementation/apple/BUILD | 5 + .../implementation/apple/ble_medium.h | 166 +++++++++ .../implementation/apple/ble_medium.mm | 325 ++++++++++++++++++ .../implementation/apple/ble_peripheral.h | 49 ++- .../implementation/apple/ble_peripheral.mm | 22 +- .../implementation/apple/ble_socket.h | 14 +- .../implementation/apple/ble_socket.mm | 7 +- internal/platform/implementation/ble_v2.h | 5 + 9 files changed, 573 insertions(+), 21 deletions(-) create mode 100644 internal/platform/implementation/apple/ble_medium.h create mode 100644 internal/platform/implementation/apple/ble_medium.mm diff --git a/Package.swift b/Package.swift index 8649eba4..ca4e39e6 100644 --- a/Package.swift +++ b/Package.swift @@ -574,6 +574,7 @@ let package = Package( // TODO(b/293283024): Stop ignoring these files when BLEv2 migration is complete. "internal/platform/implementation/apple/ble_gatt_server.mm", "internal/platform/implementation/apple/ble_gatt_client.mm", + "internal/platform/implementation/apple/ble_medium.mm", "internal/platform/implementation/apple/ble_peripheral.mm", "internal/platform/implementation/apple/ble_server_socket.mm", "internal/platform/implementation/apple/ble_socket.mm", diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 6721444a..252b664b 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -109,6 +109,7 @@ objc_library( srcs = [ "ble_gatt_client.mm", "ble_gatt_server.mm", + "ble_medium.mm", "ble_peripheral.mm", "ble_server_socket.mm", "ble_socket.mm", @@ -119,6 +120,7 @@ objc_library( hdrs = [ "ble_gatt_client.h", "ble_gatt_server.h", + "ble_medium.h", "ble_peripheral.h", "ble_server_socket.h", "ble_socket.h", @@ -130,9 +132,12 @@ objc_library( aspect_hints = ["//tools/build_defs/swift:no_module"], deps = [ "//internal/platform:base", + "//internal/platform:cancellation_flag", "//internal/platform:uuid", "//internal/platform/implementation:comm", "//internal/platform/implementation/apple/Mediums", + "//internal/platform/implementation/apple/Mediums/Ble/Sockets:Central", + "//internal/platform/implementation/apple/Mediums/Ble/Sockets:Peripheral", "//third_party/apple_frameworks:CoreBluetooth", "//third_party/apple_frameworks:Foundation", "//third_party/objective_c/google_toolbox_for_mac:GTM_Logger", diff --git a/internal/platform/implementation/apple/ble_medium.h b/internal/platform/implementation/apple/ble_medium.h new file mode 100644 index 00000000..2bfc5f9c --- /dev/null +++ b/internal/platform/implementation/apple/ble_medium.h @@ -0,0 +1,166 @@ +// 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. + +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data + +#import + +#include +#include + +#include "internal/platform/cancellation_flag.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/bluetooth_adapter.h" +#include "internal/platform/uuid.h" + +#import "internal/platform/implementation/apple/ble_peripheral.h" +#import "internal/platform/implementation/apple/ble_server_socket.h" +#import "internal/platform/implementation/apple/bluetooth_adapter_v2.h" + +@class GNCBLEMedium; +@class GNSCentralManager; +@class GNSPeripheralManager; +@class GNSPeripheralServiceManager; + +namespace nearby { +namespace apple { + +// The main BLE medium used inside of Nearby. This serves as the entry point for all BLE and GATT +// related operations. +class BleMedium : public api::ble_v2::BleMedium { + public: + BleMedium(); + ~BleMedium() override = default; + + // TODO(b/290385712): Not yet implemented. + // + // Async interface for StartAdvertising. + // + // Result status will be passed to start_advertising_result callback. To stop advertising, invoke + // the stop_advertising callback in AdvertisingSession. + // + // Advertising must be stopped before attempting to start advertising again. + std::unique_ptr StartAdvertising( + const api::ble_v2::BleAdvertisementData &advertising_data, + api::ble_v2::AdvertiseParameters advertise_set_parameters, + api::ble_v2::BleMedium::AdvertisingCallback callback) override; + + // Starts BLE advertising and returns whether or not it was successful. + // + // Advertising must be stopped before attempting to start advertising again. + bool StartAdvertising(const api::ble_v2::BleAdvertisementData &advertising_data, + api::ble_v2::AdvertiseParameters advertise_set_parameters) override; + + // TODO(b/290385712): Not yet implemented. + // + // Stops advertising. + // + // Returns whether or not advertising was successfully stopped. + bool StopAdvertising() override; + + // TODO(b/290385712): Not yet implemented. + // + // Async interface for StartScanning. + // + // Result status will be passed to start_scanning_result callback on a private queue. To stop + // scanning, invoke the stop_scanning callback in ScanningSession. + // + // Scanning must be stopped before attempting to start scanning again. + std::unique_ptr StartScanning( + const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::BleMedium::ScanningCallback callback) override; + + // Starts scanning and returns whether or not it was successful. + // + // Scanning must be stopped before attempting to start scanning again. + bool StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::BleMedium::ScanCallback callback) override; + + // TODO(b/290385712): Not yet implemented. + // + // Stops scanning. + // + // Returns whether or not scanning was successfully stopped. + bool StopScanning() override; + + // TODO(b/290385712): ServerGattConnectionCallback methods are not yet implemented. + // + // Starts a GATT server. Returns a nullptr upon error. + std::unique_ptr StartGattServer( + api::ble_v2::ServerGattConnectionCallback callback) override; + + // Connects to a GATT server and negotiates the specified connection parameters. Returns nullptr + // upon error. + // + // The peripheral must outlive the GATT client or undefined behavior will occur. The peripheral + // should not be modified by this method. + std::unique_ptr ConnectToGattServer( + api::ble_v2::BlePeripheral &peripheral, api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::ClientGattConnectionCallback callback) override; + + // Opens a BLE server socket based on service ID. + // + // On success, returns a new BleServerSocket. On error, returns nullptr. + std::unique_ptr OpenServerSocket( + const std::string &service_id) override; + + // TODO(b/290385712): cancellation_flag support is not yet implemented. + // + // Connects to a BLE peripheral. + // + // The peripheral must outlive the socket or undefined behavior will occur. The peripheral + // should not be modified by this method. + // + // On success, returns a new BleSocket. On error, returns nullptr. + std::unique_ptr Connect(const std::string &service_id, + api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::BlePeripheral &peripheral, + CancellationFlag *cancellation_flag) override; + + // Returns whether the hardware supports BOTH advertising extensions and extended scans. + // + // This is currently always false for all Apple hardware. + bool IsExtendedAdvertisementsAvailable() override; + + // A peripheral cannot be retreived via MAC address on Apple platforms. + // + // This always returns false and does not call the callback. + bool GetRemotePeripheral(const std::string &mac_address, + api::ble_v2::BleMedium::GetRemotePeripheralCallback callback) override; + + // Returns true if `id` refers to a known BLE peripheral and calls `callback` with a reference to + // said peripheral that is only guaranteed to be available for the duration of the callback. + // Otherwise, does not call the callback and returns false. + bool GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId id, + api::ble_v2::BleMedium::GetRemotePeripheralCallback callback) override; + + private: + GNCBLEMedium *medium_; + + absl::Mutex peripherals_mutex_; + absl::flat_hash_map> + peripherals_ ABSL_GUARDED_BY(peripherals_mutex_); + + GNSPeripheralServiceManager *socketPeripheralServiceManager_; + GNSPeripheralManager *socketPeripheralManager_; + GNSCentralManager *socketCentralManager_; +}; + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_medium.mm b/internal/platform/implementation/apple/ble_medium.mm new file mode 100644 index 00000000..aee27ff9 --- /dev/null +++ b/internal/platform/implementation/apple/ble_medium.mm @@ -0,0 +1,325 @@ +// 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. + +#import "internal/platform/implementation/apple/ble_medium.h" +#import "internal/platform/implementation/apple/utils.h" + +#import +#import + +#include +#include +#include +#include + +#include "internal/platform/implementation/apple/ble_utils.h" +#include "internal/platform/implementation/apple/utils.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/bluetooth_adapter.h" + +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTClient.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" +#import "internal/platform/implementation/apple/ble_gatt_client.h" +#import "internal/platform/implementation/apple/ble_gatt_server.h" +#import "internal/platform/implementation/apple/ble_peripheral.h" +#import "internal/platform/implementation/apple/ble_server_socket.h" +#import "internal/platform/implementation/apple/ble_socket.h" +#import "internal/platform/implementation/apple/bluetooth_adapter_v2.h" +#import "GoogleToolboxForMac/GTMLogger.h" + +// TODO(b/293336684): Old Weave imports that need to be deleted once shared Weave is complete. +#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleConnection.h" +#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.h" +#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Central/GNSCentralManager.h" +#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Central/GNSCentralPeerManager.h" +#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Peripheral/GNSPeripheralManager.h" +#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Peripheral/GNSPeripheralServiceManager.h" + +static NSString *const kWeaveServiceUUID = @"FEF3"; + +namespace nearby { +namespace apple { + +BleMedium::BleMedium() : medium_([[GNCBLEMedium alloc] init]) {} + +// TODO(b/290385712): Implement. +std::unique_ptr BleMedium::StartAdvertising( + const api::ble_v2::BleAdvertisementData &advertising_data, + api::ble_v2::AdvertiseParameters advertise_set_parameters, + api::ble_v2::BleMedium::AdvertisingCallback callback) { + return nullptr; +} + +bool BleMedium::StartAdvertising(const api::ble_v2::BleAdvertisementData &advertising_data, + api::ble_v2::AdvertiseParameters advertise_set_parameters) { + NSMutableDictionary *serviceData = [[NSMutableDictionary alloc] init]; + for (const auto &pair : advertising_data.service_data) { + CBUUID *key = CBUUID16FromCPP(pair.first); + NSData *data = NSDataFromByteArray(pair.second); + [serviceData setObject:data forKey:key]; + } + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block NSError *blockError = nil; + [medium_ startAdvertisingData:serviceData + completionHandler:^(NSError *error) { + if (error != nil) { + GTMLoggerError(@"Failed to start advertising: %@", error); + } + blockError = error; + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + return blockError == nil; +} + +// TODO(b/290385712): Implement. +bool BleMedium::StopAdvertising() { return false; } + +// TODO(b/290385712): Implement. +std::unique_ptr BleMedium::StartScanning( + const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::BleMedium::ScanningCallback callback) { + return nullptr; +} + +bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::BleMedium::ScanCallback callback) { + CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); + __block api::ble_v2::BleMedium::ScanCallback blockCallback = std::move(callback); + + socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; + [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]]; + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block NSError *blockError = nil; + [medium_ startScanningForService:serviceUUID + advertisementFoundHandler:^(id peripheral, + NSDictionary *serviceData) { + absl::MutexLock lock(&peripherals_mutex_); + [socketCentralManager_ retrievePeripheralWithIdentifier:peripheral.identifier + advertisementData:@{}]; + + api::ble_v2::BleAdvertisementData data; + for (CBUUID *key in serviceData.allKeys) { + data.service_data[CPPUUIDFromObjC(key)] = ByteArrayFromNSData(serviceData[key]); + } + + // Add the peripheral to the map if we haven't discovered it yet. + auto ble_peripheral = std::make_unique(peripheral); + auto unique_id = ble_peripheral->GetUniqueId(); + auto it = peripherals_.find(unique_id); + if (it == peripherals_.end()) { + peripherals_[unique_id] = std::move(ble_peripheral); + } + blockCallback.advertisement_found_cb(*peripherals_[unique_id], data); + } + completionHandler:^(NSError *error) { + if (error != nil) { + GTMLoggerError(@"Failed to start scanning: %@", error); + } + blockError = error; + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + return blockError == nil; +} + +// TODO(b/290385712): Implement. +bool BleMedium::StopScanning() { return false; } + +// TODO(b/290385712): Add implementation that calls ServerGattConnectionCallback methods. +std::unique_ptr BleMedium::StartGattServer( + api::ble_v2::ServerGattConnectionCallback callback) { + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block GNCBLEGATTServer *blockServer = nil; + [medium_ startGATTServerWithCompletionHandler:^(GNCBLEGATTServer *server, NSError *error) { + if (error != nil) { + GTMLoggerError(@"Error starting GATT server: %@", error); + } + blockServer = server; + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + if (!blockServer) { + return nullptr; + } + return std::make_unique(blockServer); +} + +std::unique_ptr BleMedium::ConnectToGattServer( + api::ble_v2::BlePeripheral &peripheral, api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::ClientGattConnectionCallback callback) { + // Check that the @c api::ble_v2::BlePeripheral is a @c nearby::apple::BlePeripheral and not a + // @c nearby::apple::EmptyBlePeripheral instance, so we can retreive the CBPeripheral object. + BlePeripheral *non_empty_peripheral = dynamic_cast(&peripheral); + if (non_empty_peripheral == nullptr) { + return nullptr; + } + + __block api::ble_v2::ClientGattConnectionCallback blockCallback = std::move(callback); + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block GNCBLEGATTClient *blockClient = nil; + [medium_ connectToGATTServerForPeripheral:non_empty_peripheral->GetPeripheral() + disconnectionHandler:^(void) { + blockCallback.disconnected_cb(); + } + completionHandler:^(GNCBLEGATTClient *client, NSError *error) { + if (error != nil) { + GTMLoggerError(@"Error connecting to GATT server: %@", error); + } + blockClient = client; + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + if (!blockClient) { + return nullptr; + } + return std::make_unique(blockClient); +} + +// 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) { + __block auto server_socket = std::make_unique(); + socketPeripheralServiceManager_ = [[GNSPeripheralServiceManager alloc] + initWithBleServiceUUID:[CBUUID UUIDWithString:kWeaveServiceUUID] + addPairingCharacteristic:NO + shouldAcceptSocketHandler:^BOOL(GNSSocket *socket) { + GNCMWaitForConnection(socket, ^(BOOL didConnect) { + GNCMBleConnection *connection = + [GNCMBleConnection connectionWithSocket:socket + serviceID:@(service_id.c_str()) + expectedIntroPacket:YES + callbackQueue:dispatch_get_main_queue()]; + + auto socket = std::make_unique(connection); + connection.connectionHandlers = socket->GetInputStream().GetConnectionHandlers(); + server_socket->Connect(std::move(socket)); + }); + return YES; + }]; + socketPeripheralManager_ = [[GNSPeripheralManager alloc] initWithAdvertisedName:nil + restoreIdentifier:nil]; + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block NSError *blockError = nil; + [socketPeripheralManager_ addPeripheralServiceManager:socketPeripheralServiceManager_ + bleServiceAddedCompletion:^(NSError *error) { + if (error != nil) { + GTMLoggerError(@"Failed to add Weave service: %@", error); + } + blockError = error; + dispatch_semaphore_signal(semaphore); + }]; + [socketPeripheralManager_ start]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + if (blockError != nil) { + return nullptr; + } + return std::move(server_socket); +} + +// TODO(b/290385712): Add support for @c cancellation_flag. +// TODO(b/293336684): Old Weave code that need to be deleted once shared Weave is complete. +std::unique_ptr BleMedium::Connect(const std::string &service_id, + api::ble_v2::TxPowerLevel tx_power_level, + api::ble_v2::BlePeripheral &peripheral, + CancellationFlag *cancellation_flag) { + // Check that the @c api::ble_v2::BlePeripheral is a @c nearby::apple::BlePeripheral and not a + // @c nearby::apple::EmptyBlePeripheral instance, so we can retreive the CBPeripheral object. + BlePeripheral *non_empty_peripheral = dynamic_cast(&peripheral); + if (non_empty_peripheral == nullptr) { + return nullptr; + } + + GNSCentralPeerManager *updatedCentralPeerManager = [socketCentralManager_ + retrieveCentralPeerWithIdentifier:non_empty_peripheral->GetPeripheral().identifier]; + if (!updatedCentralPeerManager) { + return nullptr; + } + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block std::unique_ptr socket; + [updatedCentralPeerManager + socketWithPairingCharacteristic:NO + completion:^(GNSSocket *nssocket, NSError *error) { + if (error) { + dispatch_semaphore_signal(semaphore); + return; + } + GNCMWaitForConnection(nssocket, ^(BOOL didConnect) { + if (!didConnect) { + dispatch_semaphore_signal(semaphore); + return; + } + + GNCMBleConnection *connection = [GNCMBleConnection + connectionWithSocket:nssocket + serviceID:@(service_id.c_str()) + expectedIntroPacket:NO + callbackQueue:dispatch_get_main_queue()]; + socket = + std::make_unique(connection, non_empty_peripheral); + connection.connectionHandlers = + socket->GetInputStream().GetConnectionHandlers(); + dispatch_semaphore_signal(semaphore); + }); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + if (socket == nullptr) { + return nullptr; + } + + // Send the (empty) intro packet, which the BLE advertiser is expecting. + socket->GetOutputStream().Write(ByteArray()); + return std::move(socket); +} + +bool BleMedium::IsExtendedAdvertisementsAvailable() { + return [medium_ supportsExtendedAdvertisements]; +} + +bool BleMedium::GetRemotePeripheral(const std::string &mac_address, + api::ble_v2::BleMedium::GetRemotePeripheralCallback callback) { + // Apple does not expose MAC address information, so we cannot retreive a peripheral via MAC + // address. + return false; +} + +bool BleMedium::GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId unique_id, + api::ble_v2::BleMedium::GetRemotePeripheralCallback callback) { + BlePeripheral *peripheral; + { + absl::MutexLock lock(&peripherals_mutex_); + auto it = peripherals_.find(unique_id); + if (it == peripherals_.end()) { + return false; + } + peripheral = it->second.get(); + if (peripheral == nullptr) { + return false; + } + } + // We need to unlock before calling the callback, otherwise we will deadlock. + callback(*peripheral); + return true; +} + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_peripheral.h b/internal/platform/implementation/apple/ble_peripheral.h index c17c5a4d..a1d23680 100644 --- a/internal/platform/implementation/apple/ble_peripheral.h +++ b/internal/platform/implementation/apple/ble_peripheral.h @@ -26,20 +26,24 @@ #include "internal/platform/implementation/ble_v2.h" +@protocol GNCPeripheral; + namespace nearby { namespace apple { -// Opaque wrapper over a CoreBluetooth peripheral. This can be used to uniquely -// identify a peripheral and connect to its GATT server. -class BlePeripheral : public api::ble_v2::BlePeripheral { +// An empty peripheral. +// +// Apple APIs do not expose a peripheral's MAC address and does not provide a +// way to directly connect to a given MAC address. Instead a connection can only +// be made using a CoreBluetooth peripheral object. Many times a CoreBluetooth +// peripheral is not available, namely, when the remote device is a central. For +// these cases, an EmptyBlePeripheral should be used. +class EmptyBlePeripheral : public api::ble_v2::BlePeripheral { public: - BlePeripheral() = default; - explicit BlePeripheral(CBPeripheral *peripheral); - ~BlePeripheral() override = default; + EmptyBlePeripheral(); + ~EmptyBlePeripheral() override = default; - // Returns the hardware address of this peripheral. - // - // For example, "00:11:22:AA:BB:CC". + // Returns an empty string. std::string GetAddress() const override; // Returns an immutable unique identifier. The identifier does not change when @@ -47,7 +51,32 @@ class BlePeripheral : public api::ble_v2::BlePeripheral { api::ble_v2::BlePeripheral::UniqueId GetUniqueId() const override; private: - CBPeripheral *peripheral_; + api::ble_v2::BlePeripheral::UniqueId unique_id_; +}; + +// A wrapper of a CoreBluetooth peripheral object. This can be used to uniquely +// identify a peripheral and connect to its GATT server. +// +// Many times a CoreBluetooth peripheral is not available, namely, when the +// remote device is a central. For these cases, an EmptyBlePeripheral should be +// used instead. +class BlePeripheral : public api::ble_v2::BlePeripheral { + public: + explicit BlePeripheral(id peripheral); + ~BlePeripheral() override = default; + + // Returns an empty string. + std::string GetAddress() const override; + + // Returns an immutable unique identifier. The identifier does not change when + // the peripheral's address is rotated. + api::ble_v2::BlePeripheral::UniqueId GetUniqueId() const override; + + // Returns the CoreBluetooth peripheral object. + id GetPeripheral() const; + + private: + id peripheral_; api::ble_v2::BlePeripheral::UniqueId unique_id_; }; diff --git a/internal/platform/implementation/apple/ble_peripheral.mm b/internal/platform/implementation/apple/ble_peripheral.mm index 8dedbd4e..e19646f9 100644 --- a/internal/platform/implementation/apple/ble_peripheral.mm +++ b/internal/platform/implementation/apple/ble_peripheral.mm @@ -22,17 +22,29 @@ #include "internal/platform/implementation/ble_v2.h" #include "internal/platform/prng.h" +#import "internal/platform/implementation/apple/Mediums/BLEv2/GNCPeripheral.h" + namespace nearby { namespace apple { -BlePeripheral::BlePeripheral(CBPeripheral *peripheral) - : peripheral_(peripheral), unique_id_(Prng().NextInt64()) {} +#pragma mark - EmptyBlePeripheral -std::string BlePeripheral::GetAddress() const { - return peripheral_.identifier.UUIDString.UTF8String; -} +EmptyBlePeripheral::EmptyBlePeripheral() : unique_id_(Prng().NextInt64()) {} + +std::string EmptyBlePeripheral::GetAddress() const { return ""; } + +api::ble_v2::BlePeripheral::UniqueId EmptyBlePeripheral::GetUniqueId() const { return unique_id_; } + +#pragma mark - BlePeripheral + +BlePeripheral::BlePeripheral(id peripheral) + : peripheral_(peripheral), unique_id_(peripheral.identifier.hash) {} + +std::string BlePeripheral::GetAddress() const { return ""; } api::ble_v2::BlePeripheral::UniqueId BlePeripheral::GetUniqueId() const { return unique_id_; } +id BlePeripheral::GetPeripheral() const { return peripheral_; } + } // namespace apple } // namespace nearby diff --git a/internal/platform/implementation/apple/ble_socket.h b/internal/platform/implementation/apple/ble_socket.h index 0e651719..6c64f7b5 100644 --- a/internal/platform/implementation/apple/ble_socket.h +++ b/internal/platform/implementation/apple/ble_socket.h @@ -90,7 +90,11 @@ class BleOutputStream : public OutputStream { // A BLE Weave socket. class BleSocket : public api::ble_v2::BleSocket { public: - BleSocket(id connection, BlePeripheral *peripheral); + explicit BleSocket(id connection); + + // The peripheral used to create the socket must outlive the socket or undefined behavior will + // occur. + BleSocket(id connection, api::ble_v2::BlePeripheral *peripheral); ~BleSocket() override; // Returns the InputStream of the BleSocket. @@ -98,21 +102,21 @@ class BleSocket : public api::ble_v2::BleSocket { // // The returned object is not owned by the caller, and can be invalidated once // the BleSocket object is destroyed. - InputStream &GetInputStream() override { return *input_stream_; } + BleInputStream &GetInputStream() override { return *input_stream_; } // Returns the OutputStream of the BleSocket. // On error, returned stream will report Exception::kIo on any operation. // // The returned object is not owned by the caller, and can be invalidated once // the BleSocket object is destroyed. - OutputStream &GetOutputStream() override { return *output_stream_; } + BleOutputStream &GetOutputStream() override { return *output_stream_; } // Returns Exception::kIo on error, otherwise Exception::kSuccess. Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); // Returns valid BlePeripheral pointer if there is a connection, and // nullptr otherwise. - BlePeripheral *GetRemotePeripheral() override { return peripheral_; } + api::ble_v2::BlePeripheral *GetRemotePeripheral() override { return peripheral_; } bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); @@ -123,7 +127,7 @@ class BleSocket : public api::ble_v2::BleSocket { bool closed_ ABSL_GUARDED_BY(mutex_) = false; std::unique_ptr input_stream_; std::unique_ptr output_stream_; - BlePeripheral *peripheral_; + api::ble_v2::BlePeripheral *peripheral_; }; } // namespace apple diff --git a/internal/platform/implementation/apple/ble_socket.mm b/internal/platform/implementation/apple/ble_socket.mm index a868ff0b..b150e0a9 100644 --- a/internal/platform/implementation/apple/ble_socket.mm +++ b/internal/platform/implementation/apple/ble_socket.mm @@ -177,7 +177,12 @@ Exception BleOutputStream::Close() { #pragma mark - BleSocket -BleSocket::BleSocket(id connection, BlePeripheral *peripheral) +BleSocket::BleSocket(id connection) + : input_stream_(new BleInputStream()), + output_stream_(new BleOutputStream(connection)), + peripheral_(new EmptyBlePeripheral()) {} + +BleSocket::BleSocket(id connection, api::ble_v2::BlePeripheral *peripheral) : input_stream_(new BleInputStream()), output_stream_(new BleOutputStream(connection)), peripheral_(peripheral) {} diff --git a/internal/platform/implementation/ble_v2.h b/internal/platform/implementation/ble_v2.h index 3c1bca53..f1a31b14 100644 --- a/internal/platform/implementation/ble_v2.h +++ b/internal/platform/implementation/ble_v2.h @@ -92,6 +92,8 @@ class BlePeripheral { // https://developer.android.com/reference/android/bluetooth/BluetoothDevice#getAddress() // // Returns the current address. + // + // This will always be an empty string on Apple platforms. virtual std::string GetAddress() const = 0; // Returns an immutable unique identifier. The identifier must not change when @@ -510,6 +512,9 @@ class BleMedium { // Calls `callback` and returns true if `mac_address` is a valid BLE address. // Otherwise, does not call the callback and returns false. + // + // This method is not available on Apple platforms and will always return + // false, ignoring the callback. virtual bool GetRemotePeripheral(const std::string& mac_address, GetRemotePeripheralCallback callback) = 0; From d89d1af4596d2a310f6276fb06948d1d79bbbc72 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 18 Aug 2023 17:21:41 -0700 Subject: [PATCH 112/128] [fp-rs] Added custom FP error type to Rust code. --- fastpair/rust/bluetooth/src/common/address.rs | 2 +- .../bluetooth/src/common/advertisement.rs | 2 +- .../rust/demo/lib/bridge_definitions.dart | 14 ----- fastpair/rust/demo/lib/bridge_generated.dart | 14 ----- fastpair/rust/demo/rust/Cargo.toml | 2 +- fastpair/rust/demo/rust/src/advertisement.rs | 60 ++++++------------- fastpair/rust/demo/rust/src/api.rs | 3 +- .../rust/demo/rust/src/bridge_generated.rs | 6 +- fastpair/rust/demo/rust/src/decoder.rs | 15 +++-- fastpair/rust/demo/rust/src/error.rs | 43 +++++++++++++ fastpair/rust/demo/rust/src/fetcher/common.rs | 7 +-- fastpair/rust/demo/rust/src/fetcher/fs.rs | 12 ++-- fastpair/rust/demo/rust/src/fetcher/mock.rs | 17 ++---- fastpair/rust/demo/rust/src/lib.rs | 1 + .../flutter/generated_plugin_registrant.cc | 16 +---- .../flutter/generated_plugin_registrant.h | 14 ----- 16 files changed, 97 insertions(+), 131 deletions(-) create mode 100644 fastpair/rust/demo/rust/src/error.rs diff --git a/fastpair/rust/bluetooth/src/common/address.rs b/fastpair/rust/bluetooth/src/common/address.rs index bff124d7..85f81f04 100644 --- a/fastpair/rust/bluetooth/src/common/address.rs +++ b/fastpair/rust/bluetooth/src/common/address.rs @@ -134,7 +134,7 @@ mod tests { fn try_from_ble_address_to_classic() { let ble_addr = BleAddress::new(0x112233445566, BleAddressKind::Public); let result: Result = - TryFrom::try_from(ble_addr); + ble_addr.try_into(); assert!(result.is_ok()); assert_eq!(result.unwrap().0, [0x66, 0x55, 0x44, 0x33, 0x22, 0x11]); diff --git a/fastpair/rust/bluetooth/src/common/advertisement.rs b/fastpair/rust/bluetooth/src/common/advertisement.rs index 6eb679f3..00ce6bb3 100644 --- a/fastpair/rust/bluetooth/src/common/advertisement.rs +++ b/fastpair/rust/bluetooth/src/common/advertisement.rs @@ -18,7 +18,7 @@ use super::{BleAddress, BluetoothError}; /// information about the advertisement (e.g. address of sender) as well as /// data sections extracted from the advertisement. Platform-specific methods /// should be written to load in data sections from incoming advertisements. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct BleAdvertisement { address: BleAddress, rssi: Option, diff --git a/fastpair/rust/demo/lib/bridge_definitions.dart b/fastpair/rust/demo/lib/bridge_definitions.dart index 67d21a9c..b66ccf6b 100644 --- a/fastpair/rust/demo/lib/bridge_definitions.dart +++ b/fastpair/rust/demo/lib/bridge_definitions.dart @@ -1,17 +1,3 @@ -// 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. - // AUTO GENERATED FILE, DO NOT EDIT. // Generated by `flutter_rust_bridge`@ 1.79.0. // ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const diff --git a/fastpair/rust/demo/lib/bridge_generated.dart b/fastpair/rust/demo/lib/bridge_generated.dart index 72999e9a..9fc5d9db 100644 --- a/fastpair/rust/demo/lib/bridge_generated.dart +++ b/fastpair/rust/demo/lib/bridge_generated.dart @@ -1,17 +1,3 @@ -// 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. - // AUTO GENERATED FILE, DO NOT EDIT. // Generated by `flutter_rust_bridge`@ 1.79.0. // ignore_for_file: non_constant_identifier_names, unused_element, duplicate_ignore, directives_ordering, curly_braces_in_flow_control_structures, unnecessary_lambdas, slash_for_doc_comments, prefer_const_literals_to_create_immutables, implicit_dynamic_list_literal, duplicate_import, unused_import, unnecessary_import, prefer_single_quotes, prefer_const_constructors, use_super_parameters, always_use_package_imports, annotate_overrides, invalid_use_of_protected_member, constant_identifier_names, invalid_use_of_internal_member, prefer_is_empty, unnecessary_const diff --git a/fastpair/rust/demo/rust/Cargo.toml b/fastpair/rust/demo/rust/Cargo.toml index c1839a89..4d736a04 100644 --- a/fastpair/rust/demo/rust/Cargo.toml +++ b/fastpair/rust/demo/rust/Cargo.toml @@ -9,7 +9,6 @@ edition = "2021" crate-type = ["lib", "cdylib", "staticlib"] [dependencies] -anyhow = "1.0" bluetooth = { version = "0.1", path = "../../bluetooth" } flutter_rust_bridge = "1" futures = { version = "0.3", features = ["executor"] } @@ -17,3 +16,4 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tracing = "0.1.37" ttl_cache = "0.5.1" +thiserror = "1.0.43" diff --git a/fastpair/rust/demo/rust/src/advertisement.rs b/fastpair/rust/demo/rust/src/advertisement.rs index 9179ad75..da59517b 100644 --- a/fastpair/rust/demo/rust/src/advertisement.rs +++ b/fastpair/rust/demo/rust/src/advertisement.rs @@ -14,14 +14,14 @@ use bluetooth::{BleAddress, BleAdvertisement, ServiceData}; -use crate::{decoder::FpDecoder, fetcher::FpFetcher}; +use crate::{decoder::FpDecoder, error::FpError, fetcher::FpFetcher}; /// Represents a FP device model ID. pub(crate) type ModelId = String; /// Holds information required to make decisions about an incoming Fast Pair /// advertisement. -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct FpPairingAdvertisement { inner: BleAdvertisement, /// Estimated distance in meters of device from BLE adapter. @@ -37,13 +37,15 @@ impl FpPairingAdvertisement { adv: BleAdvertisement, service_data: &ServiceData, fetcher: &Box, - ) -> Result { - let rssi = adv.rssi().ok_or(anyhow::anyhow!( - "Windows advertisements should contain RSSI information." - ))?; - let tx_power = adv.tx_power().ok_or(anyhow::anyhow!( - "Fast Pair advertisements should advertise their transmit power." - ))?; + ) -> Result { + let rssi = adv.rssi().ok_or(FpError::ContractViolation(String::from( + "Windows advertisements should contain RSSI information.", + )))?; + let tx_power = adv + .tx_power() + .ok_or(FpError::ContractViolation(String::from( + "Windows advertisements should contain RSSI information.", + )))?; let distance = distance_from_rssi_and_tx_power(rssi, tx_power); @@ -51,22 +53,16 @@ impl FpPairingAdvertisement { // data in the `FpPairingAdvertisement` since it's easily accessible from // `FpPairingAdvertisement.inner`, but it's convenient to save the parsed // model ID. - let mut model_id = - FpDecoder::get_model_id_from_service_data(service_data).or_else(|err| { - // Some FP advertisements can be GATT non-discoverable - // advertisements containing service data that isn't - // the device model ID. In this case, simply ignore - // advertisements with errors extracting the model ID. - // See: developers.google.com/nearby/fast-pair/specifications/service/provider - Err(anyhow::anyhow!("error extracting model ID: {}", err)) - })?; + let mut model_id = FpDecoder::get_model_id_from_service_data(service_data)?; if model_id.len() != 3 { // In this demo of Fast Pair Rust, only model ID's // of length 3 bytes are supported. Therefore, if a // larger model ID makes it this far, log an error. // TODO b/294453912 - return Err(anyhow::anyhow!("Error: model ID of unsupported length")); + return Err(FpError::Internal(String::from( + "creating `model_id` should have already failed", + ))); } // Pad with 0 at the beginning to successfully call `from_be_bytes`. @@ -193,6 +189,7 @@ mod tests { let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher); assert!(fp_adv.is_err()); + assert!(matches!(fp_adv.unwrap_err(), FpError::ContractViolation(_))); } #[test] @@ -212,25 +209,7 @@ mod tests { let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher); assert!(fp_adv.is_err()); - } - - #[test] - fn test_new_fp_pairing_advertisement_bad_service_data() { - let addr = BleAddress::new(0x112233, BleAddressKind::Public); - let ble_adv = BleAdvertisement::new(addr, Some(-60), Some(10)); - - let raw_data = vec![4, 3, 2, 1]; - let service_data = ServiceData::new(0x123 as u16, raw_data); - - let device_info = Ok(DeviceInfo::new( - String::from("image_url"), - String::from("name"), - )); - let fetcher: Box = Box::new(FpFetcherMock::new(device_info)); - - let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher); - - assert!(fp_adv.is_err()); + assert!(matches!(fp_adv.unwrap_err(), FpError::ContractViolation(_))); } #[test] @@ -241,12 +220,11 @@ mod tests { let raw_data = vec![3, 2, 1]; let service_data = ServiceData::new(0x123 as u16, raw_data); - let fetcher: Box = Box::new(FpFetcherMock::new(Err(anyhow::anyhow!( - "mock intentional error" - )))); + let fetcher: Box = Box::new(FpFetcherMock::new(Err(FpError::Test))); let fp_adv = FpPairingAdvertisement::new(ble_adv, &service_data, &fetcher); assert!(fp_adv.is_err()); + assert!(matches!(fp_adv.unwrap_err(), FpError::Test)); } } diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index bb1b3a2f..5948bd57 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -160,10 +160,9 @@ pub fn init() { } /// Sets up `StreamSink` for Dart-Rust FFI. -pub fn event_stream(s: StreamSink>) -> Result<(), anyhow::Error> { +pub fn event_stream(s: StreamSink>) { let mut stream = DEVICE_STREAM.write().unwrap(); *stream = Some(s); - Ok(()) } /// Attempt classic pairing with currently displayed device. diff --git a/fastpair/rust/demo/rust/src/bridge_generated.rs b/fastpair/rust/demo/rust/src/bridge_generated.rs index 06d8ce6e..bf1f040f 100644 --- a/fastpair/rust/demo/rust/src/bridge_generated.rs +++ b/fastpair/rust/demo/rust/src/bridge_generated.rs @@ -54,7 +54,11 @@ fn wire_event_stream_impl(port_: MessagePort) { mode: FfiCallMode::Stream, }, move || { - move |task_callback| event_stream(task_callback.stream_sink::<_, Option<[String; 2]>>()) + move |task_callback| { + Ok(event_stream( + task_callback.stream_sink::<_, Option<[String; 2]>>(), + )) + } }, ) } diff --git a/fastpair/rust/demo/rust/src/decoder.rs b/fastpair/rust/demo/rust/src/decoder.rs index 4865a35b..91902e20 100644 --- a/fastpair/rust/demo/rust/src/decoder.rs +++ b/fastpair/rust/demo/rust/src/decoder.rs @@ -13,6 +13,8 @@ // limitations under the License. use bluetooth::ServiceData; +use crate::error::FpError; + /// Unit struct providing parsing operations for Fast Pair advertisements. pub(crate) struct FpDecoder; @@ -25,13 +27,13 @@ impl FpDecoder { /// Currently unavailable in Fast Pair devices and not supported. pub(crate) fn get_model_id_from_service_data( service_data: &ServiceData, - ) -> Result, anyhow::Error> { + ) -> Result, FpError> { static MIN_MODEL_ID_LENGTH: usize = 3; let data = service_data.data(); if data.len() < MIN_MODEL_ID_LENGTH { // If service data too small, invalid payload. - Err(anyhow::anyhow!(format!( + Err(FpError::ContractViolation(format!( "Invalid model ID for Fast Pair advertisement of length {}.", data.len() ))) @@ -41,13 +43,14 @@ impl FpDecoder { } else { // Else, this Fast Pair advertisement is currently unsupported. // b/294453912 - Err(anyhow::anyhow!( - "This Fast Pair device is currently unsupported." - )) + Err(FpError::NotImplemented(String::from( + "This Fast Pair device is currently unsupported.", + ))) } } } +#[cfg(test)] mod tests { use super::*; @@ -70,6 +73,7 @@ mod tests { let service_data = ServiceData::new(uuid, data); let result = FpDecoder::get_model_id_from_service_data(&service_data); assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), FpError::ContractViolation(_))); } #[test] @@ -80,5 +84,6 @@ mod tests { let service_data = ServiceData::new(uuid, data); let result = FpDecoder::get_model_id_from_service_data(&service_data); assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), FpError::NotImplemented(_))); } } diff --git a/fastpair/rust/demo/rust/src/error.rs b/fastpair/rust/demo/rust/src/error.rs new file mode 100644 index 00000000..81ecb400 --- /dev/null +++ b/fastpair/rust/demo/rust/src/error.rs @@ -0,0 +1,43 @@ +// 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. + +use thiserror::Error; + +/// Library error type. +#[non_exhaustive] +#[derive(Error, Debug, PartialEq, Clone)] +pub enum FpError { + /// Reported when a requested resource could not be accessed, either because + /// it's missing or because the user lacks access permissions. + #[error("not found: {0}")] + AccessDenied(String), + /// Reported when Fast Pair functionality does not behave according to the + /// specification. For example, advertisement packets with invalid lengths, + /// JSON data with bad formatting, etc. + #[error("contract violation: {0}")] + ContractViolation(String), + /// Reported when trying to invoke Fast Pair functionality that is currently + /// not implemented. + #[error("feature not implemented: {0}")] + NotImplemented(String), + /// Reported when a bug occurs inside the library. Whenever a seemingly + /// impossible error condition arises where you could call `expect()`, + /// return this error instead. + #[error("internal error: {0}")] + Internal(String), + /// Reported when an error was intentionally raised by test code. + #[error("intentional error")] + #[cfg(test)] + Test, +} diff --git a/fastpair/rust/demo/rust/src/fetcher/common.rs b/fastpair/rust/demo/rust/src/fetcher/common.rs index ea7ed605..e3d5cf81 100644 --- a/fastpair/rust/demo/rust/src/fetcher/common.rs +++ b/fastpair/rust/demo/rust/src/fetcher/common.rs @@ -14,15 +14,12 @@ use serde::Deserialize; -use crate::advertisement::ModelId; +use crate::{advertisement::ModelId, error::FpError}; /// Types that can fetch Fast Pair data from external storage (e.g. filesystem, /// remote server). pub(crate) trait FpFetcher { - fn get_device_info_from_model_id( - &self, - model_id: &ModelId, - ) -> Result; + fn get_device_info_from_model_id(&self, model_id: &ModelId) -> Result; } /// Holds Fast Pair device information parsed from JSON. diff --git a/fastpair/rust/demo/rust/src/fetcher/fs.rs b/fastpair/rust/demo/rust/src/fetcher/fs.rs index ee45955e..df2db495 100644 --- a/fastpair/rust/demo/rust/src/fetcher/fs.rs +++ b/fastpair/rust/demo/rust/src/fetcher/fs.rs @@ -16,6 +16,7 @@ use std::fs; use crate::{ advertisement::ModelId, + error::FpError, fetcher::{DeviceInfo, FpFetcher, JsonData}, }; @@ -35,14 +36,13 @@ impl FpFetcher for FpFetcherFs { /// this information is saved locally. In the future, this should instead /// be retrieved from a remote server and cached. /// b/294456411 - fn get_device_info_from_model_id( - &self, - model_id: &ModelId, - ) -> Result { + fn get_device_info_from_model_id(&self, model_id: &ModelId) -> Result { let file_path = format!("{}/{}.json", self.path, model_id); - let contents = fs::read_to_string(file_path).expect("Couldn't find or open file."); + let contents = fs::read_to_string(file_path) + .or_else(|err| Err(FpError::AccessDenied(err.to_string())))?; - let model_info: JsonData = serde_json::from_str(&contents)?; + let model_info: JsonData = serde_json::from_str(&contents) + .or_else(|err| Err(FpError::ContractViolation(err.to_string())))?; Ok(model_info.device()) } diff --git a/fastpair/rust/demo/rust/src/fetcher/mock.rs b/fastpair/rust/demo/rust/src/fetcher/mock.rs index 93f9351f..1d82efb5 100644 --- a/fastpair/rust/demo/rust/src/fetcher/mock.rs +++ b/fastpair/rust/demo/rust/src/fetcher/mock.rs @@ -14,30 +14,25 @@ use crate::{ advertisement::ModelId, + error::FpError, fetcher::{DeviceInfo, FpFetcher}, }; /// A struct for mocking retrieval of Fast Pair data. pub(crate) struct FpFetcherMock { - get_device_info_from_model_id: Result, + device_info_from_model_id: Result, } impl FpFetcherMock { - pub(crate) fn new(get_device_info_from_model_id: Result) -> Self { + pub(crate) fn new(device_info_from_model_id: Result) -> Self { FpFetcherMock { - get_device_info_from_model_id, + device_info_from_model_id, } } } impl FpFetcher for FpFetcherMock { - fn get_device_info_from_model_id( - &self, - _model_id: &ModelId, - ) -> Result { - match &self.get_device_info_from_model_id { - Ok(result) => Ok(result.clone()), - Err(_) => Err(anyhow::anyhow!("intentional mock error")), - } + fn get_device_info_from_model_id(&self, _model_id: &ModelId) -> Result { + self.device_info_from_model_id.clone() } } diff --git a/fastpair/rust/demo/rust/src/lib.rs b/fastpair/rust/demo/rust/src/lib.rs index 34ba9081..3d3f5bfb 100644 --- a/fastpair/rust/demo/rust/src/lib.rs +++ b/fastpair/rust/demo/rust/src/lib.rs @@ -16,4 +16,5 @@ mod advertisement; mod api; mod bridge_generated; /* AUTO INJECTED BY flutter_rust_bridge. This line may not be accurate, and you can change it according to your needs. */ mod decoder; +mod error; mod fetcher; diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc index 5da9cfa4..8b6d4680 100644 --- a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc @@ -1,24 +1,10 @@ -// 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. - // // Generated file. Do not edit. // // clang-format off -#include "./generated_plugin_registrant.h" +#include "generated_plugin_registrant.h" void RegisterPlugins(flutter::PluginRegistry* registry) { diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h index 7e32ef7b..dc139d85 100644 --- a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h @@ -1,17 +1,3 @@ -// 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. - // // Generated file. Do not edit. // From 90812bfe7357ea4c37d69e68600da033994c79b7 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 18 Aug 2023 17:24:54 -0700 Subject: [PATCH 113/128] [fp-rs] Making some values const rather than static or inlined. --- fastpair/rust/demo/rust/src/api.rs | 4 +++- fastpair/rust/demo/rust/src/decoder.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index 5948bd57..09affea4 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -124,6 +124,8 @@ fn init_cache() { /// Sets up initial constructs and infinitely polls for advertisements. pub fn init() { + const JSON_PATH: &str = "./local"; + let run = async { info!("start making adapter"); @@ -134,7 +136,7 @@ pub fn init() { let mut latest_advertisement_map = HashMap::new(); let datatype_selector = vec![BleDataTypeId::ServiceData16BitUuid]; - let fetcher: Box = Box::new(FpFetcherFs::new(String::from("./local"))); + let fetcher: Box = Box::new(FpFetcherFs::new(String::from(JSON_PATH))); loop { // Retrieve the next received advertisement. diff --git a/fastpair/rust/demo/rust/src/decoder.rs b/fastpair/rust/demo/rust/src/decoder.rs index 91902e20..2083f83a 100644 --- a/fastpair/rust/demo/rust/src/decoder.rs +++ b/fastpair/rust/demo/rust/src/decoder.rs @@ -28,7 +28,7 @@ impl FpDecoder { pub(crate) fn get_model_id_from_service_data( service_data: &ServiceData, ) -> Result, FpError> { - static MIN_MODEL_ID_LENGTH: usize = 3; + const MIN_MODEL_ID_LENGTH: usize = 3; let data = service_data.data(); if data.len() < MIN_MODEL_ID_LENGTH { From 5f255f1ff5b847d6b7f36ec508554b5dc529b386 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 18 Aug 2023 17:29:38 -0700 Subject: [PATCH 114/128] [fp-rs] Fixing build warnings by removing or annotating unused imports. --- .../rust/bluetooth/src/windows/adapter.rs | 2 -- fastpair/rust/bluetooth/src/windows/device.rs | 2 -- .../rust/bluetooth/tests/integration_test.rs | 21 ------------------- fastpair/rust/demo/rust/src/fetcher/common.rs | 4 ++++ 4 files changed, 4 insertions(+), 25 deletions(-) delete mode 100644 fastpair/rust/bluetooth/tests/integration_test.rs diff --git a/fastpair/rust/bluetooth/src/windows/adapter.rs b/fastpair/rust/bluetooth/src/windows/adapter.rs index 7130cb97..81613411 100644 --- a/fastpair/rust/bluetooth/src/windows/adapter.rs +++ b/fastpair/rust/bluetooth/src/windows/adapter.rs @@ -219,7 +219,5 @@ impl api::BleAdapter for BleAdapter { } mod tests { - use super::*; - // TODO b/288592509 unit tests } diff --git a/fastpair/rust/bluetooth/src/windows/device.rs b/fastpair/rust/bluetooth/src/windows/device.rs index a3d2d649..c0b96d8d 100644 --- a/fastpair/rust/bluetooth/src/windows/device.rs +++ b/fastpair/rust/bluetooth/src/windows/device.rs @@ -157,7 +157,5 @@ impl api::ClassicDevice for ClassicDevice { } mod tests { - use super::*; - // TODO b/288592509 unit tests } diff --git a/fastpair/rust/bluetooth/tests/integration_test.rs b/fastpair/rust/bluetooth/tests/integration_test.rs deleted file mode 100644 index b3332b2a..00000000 --- a/fastpair/rust/bluetooth/tests/integration_test.rs +++ /dev/null @@ -1,21 +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. - -use bluetooth::*; - -mod tests { - use super::*; - - // TODO b/288592509 write integration tests -} diff --git a/fastpair/rust/demo/rust/src/fetcher/common.rs b/fastpair/rust/demo/rust/src/fetcher/common.rs index e3d5cf81..d2bc13f4 100644 --- a/fastpair/rust/demo/rust/src/fetcher/common.rs +++ b/fastpair/rust/demo/rust/src/fetcher/common.rs @@ -38,6 +38,10 @@ pub(super) struct JsonData { } impl DeviceInfo { + // `new()` is conceivably only used in tests, since this struct should be + // constructed by serde_json. Therefore, adding cfg to disable dead code + // warnings. Can be removed in the future. + #[cfg(test)] pub(crate) fn new(image_url: String, name: String) -> Self { DeviceInfo { image_url, name } } From 02e9cda69856a74e7cfc1ff85df4fa4d79307ab5 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 18 Aug 2023 17:56:19 -0700 Subject: [PATCH 115/128] [fp-rs] Comment cleanup. --- fastpair/rust/demo/rust/src/api.rs | 22 ++++++++++++++++++- fastpair/rust/demo/rust/src/decoder.rs | 1 + .../flutter/generated_plugin_registrant.cc | 14 ++++++++++++ .../flutter/generated_plugin_registrant.h | 14 ++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/fastpair/rust/demo/rust/src/api.rs b/fastpair/rust/demo/rust/src/api.rs index 09affea4..40d69038 100644 --- a/fastpair/rust/demo/rust/src/api.rs +++ b/fastpair/rust/demo/rust/src/api.rs @@ -1,3 +1,17 @@ +// 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. + use std::{collections::HashMap, sync::RwLock, time::Duration}; use bluetooth::{ @@ -23,7 +37,7 @@ static CURR_DEVICE_ADV: RwLock> = RwLock::new(Non // Temporarily restricts which model IDs can be displayed. static MODEL_ID_BLACKLIST: RwLock>> = RwLock::new(None); -// How long entries should blacklisted for for. +// Specifies how long entries should blacklisted for. const TTL_BLACKLIST: Duration = Duration::from_secs(10); /// Updates the device name as displayed by Flutter. @@ -83,6 +97,8 @@ fn new_best_fp_advertisement( return None; } + // Else, this advertisement is now the most recent advertisement by the + // device with model ID `fp_adv.model_id()`. latest_advertisement_map.insert(fp_adv.model_id().to_owned(), fp_adv.clone()); if let Some(best_adv) = CURR_DEVICE_ADV.read().unwrap().as_ref() { @@ -101,6 +117,7 @@ fn new_best_fp_advertisement( adv1.distance().partial_cmp(&adv2.distance()).unwrap() }); + // If next closest device exists, it's now the closest! if let Some(next_best_adv) = next_best_adv_ref { Some(next_best_adv.to_owned()) } else { @@ -206,14 +223,17 @@ pub fn dismiss() { let mut adv = CURR_DEVICE_ADV.write().unwrap(); match MODEL_ID_BLACKLIST.write().unwrap().as_mut() { Some(cache) => { + // Get rid of the best (i.e. currently displayed) device. let adv = adv.take(); match adv { Some(adv) => { + // Add device to TTL blacklist. cache.insert(adv.model_id().to_string(), (), TTL_BLACKLIST); } None => (), } + // Ensure the currently-displayed device is no longer displayed. match DEVICE_STREAM.read().unwrap().as_ref() { Some(stream) => { stream.add(None); diff --git a/fastpair/rust/demo/rust/src/decoder.rs b/fastpair/rust/demo/rust/src/decoder.rs index 2083f83a..fd190557 100644 --- a/fastpair/rust/demo/rust/src/decoder.rs +++ b/fastpair/rust/demo/rust/src/decoder.rs @@ -11,6 +11,7 @@ // 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 bluetooth::ServiceData; use crate::error::FpError; diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc index 8b6d4680..95e96008 100644 --- a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc @@ -1,3 +1,17 @@ +// 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. + // // Generated file. Do not edit. // diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h index dc139d85..7e32ef7b 100644 --- a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.h @@ -1,3 +1,17 @@ +// 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. + // // Generated file. Do not edit. // From c7c59f19208b9f036ce0281ae2e301aa460f1a43 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 18 Aug 2023 18:12:39 -0700 Subject: [PATCH 116/128] [fp-rs] Replacing cargo build with cargo test in validate. --- .github/workflows/validate.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 24e0da2a..25d7556e 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -48,9 +48,9 @@ jobs: - name: Build FPP run: cargo build --manifest-path presence/fpp/fpp/Cargo.toml - name: Build Bluetooth Module - run: cargo build --manifest-path fastpair/rust/bluetooth/Cargo.toml + run: cargo test --manifest-path fastpair/rust/bluetooth/Cargo.toml - name: Build Fast Pair - run: cargo build --manifest-path fastpair/rust/demo/rust/Cargo.toml + run: cargo test --manifest-path fastpair/rust/demo/rust/Cargo.toml build-rust-windows: name: Build Rust on Windows @@ -60,7 +60,7 @@ jobs: with: submodules: recursive - name: Build Bluetooth Module - run: cargo build --manifest-path fastpair/rust/bluetooth/Cargo.toml + run: cargo test --manifest-path fastpair/rust/bluetooth/Cargo.toml - name: Build Fast Pair - run: cargo build --manifest-path fastpair/rust/demo/rust/Cargo.toml + run: cargo test --manifest-path fastpair/rust/demo/rust/Cargo.toml \ No newline at end of file From 22e7900bb359e3ef452a1a83f85c4831c673242a Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Fri, 18 Aug 2023 20:12:00 -0700 Subject: [PATCH 117/128] [fp-rs] Fixing Presubmit linter issues for autogenerated Flutter code. --- .../rust/demo/windows/flutter/generated_plugin_registrant.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc index 95e96008..5da9cfa4 100644 --- a/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc +++ b/fastpair/rust/demo/windows/flutter/generated_plugin_registrant.cc @@ -18,7 +18,7 @@ // clang-format off -#include "generated_plugin_registrant.h" +#include "./generated_plugin_registrant.h" void RegisterPlugins(flutter::PluginRegistry* registry) { From 71b5c1d59118509c4679dcb1cdef8bb352ef6513 Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Mon, 21 Aug 2023 14:10:49 -0700 Subject: [PATCH 118/128] Put some logs in session manager PiperOrigin-RevId: 558896551 --- .../implementation/windows/session_manager.cc | 131 +++++++++++------- .../implementation/windows/session_manager.h | 13 ++ 2 files changed, 94 insertions(+), 50 deletions(-) diff --git a/internal/platform/implementation/windows/session_manager.cc b/internal/platform/implementation/windows/session_manager.cc index 09c207b9..3624a3ae 100644 --- a/internal/platform/implementation/windows/session_manager.cc +++ b/internal/platform/implementation/windows/session_manager.cc @@ -18,8 +18,10 @@ #include #include +#include #include "absl/base/attributes.h" +#include "absl/base/const_init.h" #include "absl/container/flat_hash_map.h" #include "absl/functional/any_invocable.h" #include "absl/synchronization/mutex.h" @@ -34,36 +36,24 @@ namespace { constexpr char kMessageWindowClass[] = "Nearby Message Window Class"; constexpr char kMessageWindowTitle[] = "Nearby Message Dummy Window"; -// Define global static variables. -ABSL_CONST_INIT absl::Mutex kSessionMutex(absl::kConstInit); -HWND kSessionHwnd = nullptr; -SubmittableExecutor* kSessionThread = nullptr; -absl::flat_hash_map>* - kSessionCallbacks = nullptr; - LRESULT CALLBACK NearbyWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_DESTROY: PostQuitMessage(0); - return 0; + break; case WM_WTSSESSION_CHANGE: if (wParam == WTS_SESSION_LOCK) { - absl::MutexLock lock(&kSessionMutex); - for (auto& it : *kSessionCallbacks) { - it.second(SessionManager::SessionState::kLock); - } + SessionManager::NotifySessionState(SessionManager::SessionState::kLock); } else if (wParam == WTS_SESSION_UNLOCK) { - absl::MutexLock lock(&kSessionMutex); - for (auto& it : *kSessionCallbacks) { - it.second(SessionManager::SessionState::kUnlock); - } + SessionManager::NotifySessionState( + SessionManager::SessionState::kUnlock); } - return 0; - default: - return DefWindowProc(hwnd, uMsg, wParam, lParam); + break; } + + // NOLINTNEXTLINE(misc-include-cleaner) + return DefWindowProc(hwnd, uMsg, wParam, lParam); } HWND CreateNearbyWindow() { @@ -87,58 +77,75 @@ HWND CreateNearbyWindow() { } // namespace +// Initialize class static variables. +ABSL_CONST_INIT absl::Mutex SessionManager::session_mutex_{absl::kConstInit}; +HWND SessionManager::session_hwnd_ = nullptr; +SubmittableExecutor* SessionManager::session_thread_ = nullptr; +absl::flat_hash_map>* + SessionManager::session_callbacks_ = nullptr; + SessionManager::~SessionManager() { StopSession(); } bool SessionManager::RegisterSessionListener( absl::string_view listener_name, absl::AnyInvocable callback) { - absl::MutexLock lock(&kSessionMutex); + absl::MutexLock lock(&session_mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Registering listener: " << listener_name; // Create session thread if no running thread. - if (kSessionThread == nullptr) { + if (session_thread_ == nullptr) { absl::Notification notification; - kSessionThread = new SubmittableExecutor(); - kSessionCallbacks = new absl::flat_hash_map< + session_thread_ = new SubmittableExecutor(); + session_callbacks_ = new absl::flat_hash_map< std::string, absl::AnyInvocable>(); - kSessionThread->Execute( + session_thread_->Execute( [this, ¬ification]() { StartSession(notification); }); notification.WaitForNotification(); - if (kSessionThread == nullptr) { + if (session_thread_ == nullptr) { return false; } } - if (kSessionCallbacks->contains(listener_name) || + if (session_callbacks_->contains(listener_name) || listeners_.contains(listener_name)) { return false; } - kSessionCallbacks->emplace(listener_name, std::move(callback)); + session_callbacks_->emplace(listener_name, std::move(callback)); listeners_.emplace(listener_name); + NEARBY_LOGS(INFO) << __func__ << ": Session listener: " << listener_name + << " is registered."; return true; } bool SessionManager::UnregisterSessionListener( absl::string_view listener_name) { - absl::MutexLock lock(&kSessionMutex); - if (kSessionThread == nullptr) { + absl::MutexLock lock(&session_mutex_); + NEARBY_LOGS(INFO) << __func__ + << ": Unregistering listener: " << listener_name; + if (session_thread_ == nullptr) { NEARBY_LOGS(ERROR) << __func__ << ": No running listener."; return false; } - if (!kSessionCallbacks->contains(listener_name) || + if (!session_callbacks_->contains(listener_name) || !listeners_.contains(listener_name)) { NEARBY_LOGS(ERROR) << __func__ << ": No listener with name:" << listener_name; return false; } - kSessionCallbacks->erase(listener_name); + session_callbacks_->erase(listener_name); listeners_.erase(listener_name); - if (!kSessionCallbacks->empty()) { + if (!session_callbacks_->empty()) { + NEARBY_LOGS(INFO) << __func__ << ": Session listener: " << listener_name + << " is unregistered."; return true; } CleanUp(); + NEARBY_LOGS(INFO) << __func__ << ": Session listener: " << listener_name + << " is unregistered."; return true; } @@ -188,14 +195,30 @@ bool SessionManager::AllowSleep() const { return true; } +void SessionManager::NotifySessionState(SessionState state) { + NEARBY_LOGS(INFO) << __func__ + << ": Notifying session state: " << static_cast(state); + if (state == SessionManager::SessionState::kLock) { + absl::MutexLock lock(&session_mutex_); + for (auto& it : *SessionManager::session_callbacks_) { + it.second(SessionManager::SessionState::kLock); + } + } else if (state == SessionManager::SessionState::kUnlock) { + absl::MutexLock lock(&session_mutex_); + for (auto& it : *SessionManager::session_callbacks_) { + it.second(SessionManager::SessionState::kUnlock); + } + } +} + void SessionManager::StartSession(absl::Notification& notification) { - kSessionHwnd = CreateNearbyWindow(); - if (kSessionHwnd == nullptr) { + session_hwnd_ = CreateNearbyWindow(); + if (session_hwnd_ == nullptr) { NEARBY_LOGS(ERROR) << __func__ << ": Failed to create session Window."; return; } - if (!WTSRegisterSessionNotification(kSessionHwnd, NOTIFY_FOR_THIS_SESSION)) { + if (!WTSRegisterSessionNotification(session_hwnd_, NOTIFY_FOR_THIS_SESSION)) { NEARBY_LOGS(ERROR) << __func__ << ":Failed to register session notification."; return; @@ -203,14 +226,17 @@ void SessionManager::StartSession(absl::Notification& notification) { notification.Notify(); + NEARBY_LOGS(INFO) << __func__ << ": Session thread started."; + // Main message loop MSG msg = {}; - while (GetMessage(&msg, nullptr, 0, 0)) { + // NOLINTNEXTLINE(misc-include-cleaner) + while (GetMessage(&msg, session_hwnd_, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } - if (!WTSUnRegisterSessionNotification(kSessionHwnd)) { + if (!WTSUnRegisterSessionNotification(session_hwnd_)) { NEARBY_LOGS(ERROR) << __func__ << ": Failed to register session notification."; return; @@ -225,16 +251,20 @@ void SessionManager::StartSession(absl::Notification& notification) { } void SessionManager::StopSession() { - absl::MutexLock lock(&kSessionMutex); - if (kSessionThread == nullptr) { + if (session_thread_ == nullptr) { + return; + } + + absl::MutexLock lock(&session_mutex_); + if (session_thread_ == nullptr) { return; } for (const auto& it : listeners_) { - kSessionCallbacks->erase(it); + session_callbacks_->erase(it); } listeners_.clear(); - if (!kSessionCallbacks->empty()) { + if (!session_callbacks_->empty()) { return; } @@ -242,17 +272,18 @@ void SessionManager::StopSession() { } void SessionManager::CleanUp() { - if (kSessionHwnd != nullptr) { + if (session_hwnd_ != nullptr) { // Send message to destroy message window. - PostMessageA(kSessionHwnd, WM_DESTROY, 0, 0); + // NOLINTNEXTLINE(misc-include-cleaner) + PostMessageA(session_hwnd_, WM_DESTROY, 0, 0); } - kSessionThread->Shutdown(); - delete kSessionThread; - delete kSessionCallbacks; - kSessionThread = nullptr; - kSessionCallbacks = nullptr; - kSessionHwnd = nullptr; + session_thread_->Shutdown(); + delete session_thread_; + delete session_callbacks_; + session_thread_ = nullptr; + session_callbacks_ = nullptr; + session_hwnd_ = nullptr; } } // namespace windows diff --git a/internal/platform/implementation/windows/session_manager.h b/internal/platform/implementation/windows/session_manager.h index a5da2c26..e3faf4b8 100644 --- a/internal/platform/implementation/windows/session_manager.h +++ b/internal/platform/implementation/windows/session_manager.h @@ -17,10 +17,13 @@ #include +#include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" #include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" #include "absl/synchronization/notification.h" +#include "internal/platform/implementation/windows/submittable_executor.h" namespace nearby { namespace windows { @@ -49,12 +52,22 @@ class SessionManager { // Allows the Windows device to sleep state. bool AllowSleep() const; + static void NotifySessionState(SessionState state); + private: void StartSession(absl::Notification& notification); void StopSession(); void CleanUp(); absl::flat_hash_set listeners_; + + // Static class members + static absl::Mutex session_mutex_; + static HWND session_hwnd_; + static SubmittableExecutor* session_thread_; + static absl::flat_hash_map< + std::string, absl::AnyInvocable>* + session_callbacks_; }; } // namespace windows From 96750db70681b52f185764c935b29b53da236f7a Mon Sep 17 00:00:00 2001 From: Anthony Rueda Date: Mon, 21 Aug 2023 15:38:09 -0700 Subject: [PATCH 119/128] [Presence] Rename`metadata_encryption_key_tag` fields in shared credential proto to differentiate between V0/V1 unsigned adv key tags PiperOrigin-RevId: 558919544 --- internal/proto/credential.proto | 8 ++++---- presence/implementation/advertisement_decoder.cc | 2 +- presence/implementation/advertisement_decoder_test.cc | 2 +- presence/implementation/credential_manager_impl.cc | 2 +- presence/implementation/credential_manager_impl_test.cc | 2 +- presence/implementation/ldt_test.cc | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/internal/proto/credential.proto b/internal/proto/credential.proto index f7236d48..c334d24c 100644 --- a/internal/proto/credential.proto +++ b/internal/proto/credential.proto @@ -62,8 +62,8 @@ message SharedCredential { // metadata_encryption_key. bytes encrypted_metadata_bytes_v0 = 5; - // The tag for verifying metadata_encryption_key for an unsigned adv. - bytes metadata_encryption_key_unsigned_adv_tag = 6; + // The tag for verifying metadata_encryption_key for a V0 advertisement. + bytes metadata_encryption_key_tag_v0 = 6; // The public key is used to create a secure connection with the device. bytes connection_signature_verification_key = 7; @@ -92,8 +92,8 @@ message SharedCredential { // metadata_encryption_key. bytes encrypted_metadata_bytes_v1 = 12; - // The tag for verifying metadata_encryption_key for a signed V1 adv. - bytes metadata_encryption_key_signed_adv_tag = 13; + // The tag for verifying metadata_encryption_key for an unsigned V1 adv. + bytes metadata_encryption_key_unsigned_adv_tag_v1 = 13; // The randomly generated positive unique id of the shared credential. int64 id = 14; diff --git a/presence/implementation/advertisement_decoder.cc b/presence/implementation/advertisement_decoder.cc index ff292cb2..53102ac9 100644 --- a/presence/implementation/advertisement_decoder.cc +++ b/presence/implementation/advertisement_decoder.cc @@ -214,7 +214,7 @@ absl::StatusOr AdvertisementDecoder::DecryptLdt( for (const auto& credential : credentials) { absl::StatusOr encryptor = LdtEncryptor::Create( credential.key_seed(), - credential.metadata_encryption_key_unsigned_adv_tag()); + credential.metadata_encryption_key_tag_v0()); if (encryptor.ok()) { absl::StatusOr result = encryptor->DecryptAndVerify(data_elements, salt); diff --git a/presence/implementation/advertisement_decoder_test.cc b/presence/implementation/advertisement_decoder_test.cc index 0807cad4..8b532ef7 100644 --- a/presence/implementation/advertisement_decoder_test.cc +++ b/presence/implementation/advertisement_decoder_test.cc @@ -78,7 +78,7 @@ SharedCredential GetPublicCredential() { 163, 203, 100, 235, 53, 65, 202, 97, 75, 180}); SharedCredential public_credential; public_credential.set_key_seed(seed.AsStringView()); - public_credential.set_metadata_encryption_key_unsigned_adv_tag( + public_credential.set_metadata_encryption_key_tag_v0( known_mac.AsStringView()); return public_credential; } diff --git a/presence/implementation/credential_manager_impl.cc b/presence/implementation/credential_manager_impl.cc index 7696ce0b..f5bafad8 100644 --- a/presence/implementation/credential_manager_impl.cc +++ b/presence/implementation/credential_manager_impl.cc @@ -245,7 +245,7 @@ SharedCredential CredentialManagerImpl::CreatePublicCredential( auto metadata_encryption_key_tag = Crypto::Sha256(private_credential.metadata_encryption_key_v0()); - public_credential.set_metadata_encryption_key_unsigned_adv_tag( + public_credential.set_metadata_encryption_key_tag_v0( std::string(metadata_encryption_key_tag.AsStringView())); // Encrypt the device metadata diff --git a/presence/implementation/credential_manager_impl_test.cc b/presence/implementation/credential_manager_impl_test.cc index a19f4eac..2d3b34bf 100644 --- a/presence/implementation/credential_manager_impl_test.cc +++ b/presence/implementation/credential_manager_impl_test.cc @@ -175,7 +175,7 @@ TEST_F(CredentialManagerImplTest, CreateOneCredentialSuccessfully) { absl::ToUnixMillis(kEndTime + absl::Hours(3))); EXPECT_EQ(Crypto::Sha256(private_credential.metadata_encryption_key_v0()) .AsStringView(), - public_credential.metadata_encryption_key_unsigned_adv_tag()); + 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()); diff --git a/presence/implementation/ldt_test.cc b/presence/implementation/ldt_test.cc index 88eadfe8..fbbd2581 100644 --- a/presence/implementation/ldt_test.cc +++ b/presence/implementation/ldt_test.cc @@ -89,7 +89,7 @@ TEST(Ldt, DecryptAndroidData) { absl::HexStringToBytes(kSharedCredentialBase16))); absl::StatusOr encryptor = LdtEncryptor::Create( shared_credential.key_seed(), - shared_credential.metadata_encryption_key_unsigned_adv_tag()); + shared_credential.metadata_encryption_key_tag_v0()); ASSERT_OK(encryptor); absl::StatusOr decrypted = From ea7aa00e0cd99a0fab900ae55e727fd5acf672fd Mon Sep 17 00:00:00 2001 From: Guogang Li Date: Mon, 21 Aug 2023 17:22:37 -0700 Subject: [PATCH 120/128] Avoid reading GATT server for same advertisement PiperOrigin-RevId: 558944228 --- .../implementation/mediums/ble_v2/BUILD | 1 + .../ble_v2/discovered_peripheral_tracker.cc | 26 ++++++++++++++++++- .../ble_v2/discovered_peripheral_tracker.h | 7 +++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/connections/implementation/mediums/ble_v2/BUILD b/connections/implementation/mediums/ble_v2/BUILD index cd9321e8..17241f9e 100644 --- a/connections/implementation/mediums/ble_v2/BUILD +++ b/connections/implementation/mediums/ble_v2/BUILD @@ -50,6 +50,7 @@ cc_library( "//internal/platform:uuid", "//proto/mediums:ble_frames_cc_proto", "@aappleby_smhasher//:libmurmur3", + "@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/numeric:int128", diff --git a/connections/implementation/mediums/ble_v2/discovered_peripheral_tracker.cc b/connections/implementation/mediums/ble_v2/discovered_peripheral_tracker.cc index 3153ca1f..806cf3dc 100644 --- a/connections/implementation/mediums/ble_v2/discovered_peripheral_tracker.cc +++ b/connections/implementation/mediums/ble_v2/discovered_peripheral_tracker.cc @@ -30,6 +30,7 @@ #include "connections/implementation/mediums/ble_v2/bloom_filter.h" #include "internal/flags/nearby_flags.h" #include "internal/platform/ble_v2.h" +#include "internal/platform/byte_array.h" #include "internal/platform/logging.h" #include "internal/platform/multi_thread_executor.h" #include "internal/platform/mutex_lock.h" @@ -521,13 +522,35 @@ void DiscoveredPeripheralTracker::HandleAdvertisementHeader( << absl::BytesToHexString( ByteArray(advertisement_header).data()) << " in thread"; + ByteArray advertisement_data{advertisement_header}; + if (fetching_advertisements_.contains(advertisement_data)) { + NEARBY_LOGS(VERBOSE) << ": Ignore the advertisement header due to it " + "is already in fetcing."; + return; + } + + fetching_advertisements_.insert(advertisement_data); + if (executor_ == nullptr) { // The situation happens when flag value changed executor_ = std::make_unique(kGattThreadCount); } executor_->Execute([this, peripheral, advertisement_header, advertisement_fetcher = - std::move(advertisement_fetcher)]() { + std::move(advertisement_fetcher), + advertisement_data = + std::move(advertisement_data)]() { + { + MutexLock lock(&mutex_); + if (!IsInterestingAdvertisementHeader(advertisement_header)) { + NEARBY_LOGS(INFO) + << ": Ignore to read raw advertisement from server due to it " + "is not interesting header now."; + fetching_advertisements_.erase(advertisement_data); + return; + } + } + std::vector gatt_advertisement_bytes_list = FetchRawAdvertisementsInThread(peripheral, advertisement_header, std::move(advertisement_fetcher)); @@ -537,6 +560,7 @@ void DiscoveredPeripheralTracker::HandleAdvertisementHeader( gatt_advertisement_bytes_list, /*service_uuid=*/{}); UpdateCommonStateForFoundBleAdvertisement(advertisement_header); + fetching_advertisements_.erase(advertisement_data); NEARBY_LOGS(VERBOSE) << ": Completed to handle GATT advertisement " << absl::BytesToHexString(ByteArray(advertisement_header).data()) diff --git a/connections/implementation/mediums/ble_v2/discovered_peripheral_tracker.h b/connections/implementation/mediums/ble_v2/discovered_peripheral_tracker.h index 423514cb..fcd59951 100644 --- a/connections/implementation/mediums/ble_v2/discovered_peripheral_tracker.h +++ b/connections/implementation/mediums/ble_v2/discovered_peripheral_tracker.h @@ -20,6 +20,9 @@ #include #include +#include "absl/base/thread_annotations.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" #include "connections/implementation/mediums//lost_entity_tracker.h" #include "connections/implementation/mediums/ble_v2/advertisement_read_result.h" #include "connections/implementation/mediums/ble_v2/ble_advertisement.h" @@ -289,6 +292,10 @@ class DiscoveredPeripheralTracker { absl::flat_hash_map gatt_advertisement_infos_ ABSL_GUARDED_BY(mutex_); + // Tracks the advertisements in GATT fetching. + absl::flat_hash_set fetching_advertisements_ + ABSL_GUARDED_BY(mutex_); + std::unique_ptr executor_ ABSL_GUARDED_BY(mutex_) = nullptr; }; From 06a8149268fbc47683e87c70a8f009c0abc3c857 Mon Sep 17 00:00:00 2001 From: Anay Wadhera Date: Tue, 22 Aug 2023 13:58:10 -0400 Subject: [PATCH 121/128] Add compiled presence_frame proto --- .../presence/proto/presence_frame.pb.cc | 4258 +++++++++++++ .../presence/proto/presence_frame.pb.h | 5274 +++++++++++++++++ 2 files changed, 9532 insertions(+) create mode 100644 compiled_proto/presence/proto/presence_frame.pb.cc create mode 100644 compiled_proto/presence/proto/presence_frame.pb.h diff --git a/compiled_proto/presence/proto/presence_frame.pb.cc b/compiled_proto/presence/proto/presence_frame.pb.cc new file mode 100644 index 00000000..f1340dee --- /dev/null +++ b/compiled_proto/presence/proto/presence_frame.pb.cc @@ -0,0 +1,4258 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: presence/proto/presence_frame.proto + +#include "presence/proto/presence_frame.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include + +PROTOBUF_PRAGMA_INIT_SEG +namespace nearby { +namespace presence { +constexpr PresenceFrame::PresenceFrame( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : v1_frame_(nullptr){} +struct PresenceFrameDefaultTypeInternal { + constexpr PresenceFrameDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~PresenceFrameDefaultTypeInternal() {} + union { + PresenceFrame _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PresenceFrameDefaultTypeInternal _PresenceFrame_default_instance_; +constexpr V1Frame::V1Frame( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : _oneof_case_{}{} +struct V1FrameDefaultTypeInternal { + constexpr V1FrameDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~V1FrameDefaultTypeInternal() {} + union { + V1Frame _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT V1FrameDefaultTypeInternal _V1Frame_default_instance_; +constexpr DeviceIdentityFrame::DeviceIdentityFrame( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : action_() + , _action_cached_byte_size_(0) + , device_name_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , bluetooth_mac_address_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , device_image_url_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , model_id_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , device_model_name_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , device_type_(0){} +struct DeviceIdentityFrameDefaultTypeInternal { + constexpr DeviceIdentityFrameDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~DeviceIdentityFrameDefaultTypeInternal() {} + union { + DeviceIdentityFrame _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT DeviceIdentityFrameDefaultTypeInternal _DeviceIdentityFrame_default_instance_; +constexpr ConnectionInitFrame::ConnectionInitFrame( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : actions_() + , _actions_cached_byte_size_(0) + , identity_type_(0) + , uwb_enable_(false) + , device_unique_id_(int64_t{0}){} +struct ConnectionInitFrameDefaultTypeInternal { + constexpr ConnectionInitFrameDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~ConnectionInitFrameDefaultTypeInternal() {} + union { + ConnectionInitFrame _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT ConnectionInitFrameDefaultTypeInternal _ConnectionInitFrame_default_instance_; +constexpr UwbControleeCapabilities::UwbControleeCapabilities( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : supported_config_ids_() + , _supported_config_ids_cached_byte_size_(0) + , supported_channels_() + , _supported_channels_cached_byte_size_(0) + , supported_ntf_configs_() + , _supported_ntf_configs_cached_byte_size_(0) + , supported_slot_durations_() + , _supported_slot_durations_cached_byte_size_(0) + , supported_ranging_update_rates_() + , _supported_ranging_update_rates_cached_byte_size_(0) + , multi_chip_info_() + , controlee_address_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , sub_session_id_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , sub_session_key_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , device_unique_id_(int64_t{0}) + , min_ranging_interval_ms_(0) + , ranging_disabled_(false) + , is_elevation_supported_(false) + , is_ranging_interval_reconfigure_supported_(false) + , chip_count_(1) + , is_distance_supported_(true) + , is_azimuth_supported_(true) + , min_slot_duration_ms_(2){} +struct UwbControleeCapabilitiesDefaultTypeInternal { + constexpr UwbControleeCapabilitiesDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~UwbControleeCapabilitiesDefaultTypeInternal() {} + union { + UwbControleeCapabilities _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT UwbControleeCapabilitiesDefaultTypeInternal _UwbControleeCapabilities_default_instance_; +constexpr UwbMultiChipInfo::UwbMultiChipInfo( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : controlee_address_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , chip_id_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string){} +struct UwbMultiChipInfoDefaultTypeInternal { + constexpr UwbMultiChipInfoDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~UwbMultiChipInfoDefaultTypeInternal() {} + union { + UwbMultiChipInfo _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT UwbMultiChipInfoDefaultTypeInternal _UwbMultiChipInfo_default_instance_; +constexpr UwbConnectionInfo::UwbConnectionInfo( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : controller_address_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , vendor_id_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , static_sts_iv_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , session_key_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , channel_(0) + , preamble_index_(0) + , config_id_(0) + , ranging_interval_ms_(0) + , session_id_(0) + , ranging_disabled_(false){} +struct UwbConnectionInfoDefaultTypeInternal { + constexpr UwbConnectionInfoDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~UwbConnectionInfoDefaultTypeInternal() {} + union { + UwbConnectionInfo _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT UwbConnectionInfoDefaultTypeInternal _UwbConnectionInfo_default_instance_; +constexpr ControlFrame::ControlFrame( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : type_(0) +{} +struct ControlFrameDefaultTypeInternal { + constexpr ControlFrameDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~ControlFrameDefaultTypeInternal() {} + union { + ControlFrame _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT ControlFrameDefaultTypeInternal _ControlFrame_default_instance_; +constexpr PresenceAuthenticationFrame::PresenceAuthenticationFrame( + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized) + : private_key_signature_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , shared_credential_id_hash_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , credential_id_hash_(&::PROTOBUF_NAMESPACE_ID::internal::fixed_address_empty_string) + , version_(0){} +struct PresenceAuthenticationFrameDefaultTypeInternal { + constexpr PresenceAuthenticationFrameDefaultTypeInternal() + : _instance(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized{}) {} + ~PresenceAuthenticationFrameDefaultTypeInternal() {} + union { + PresenceAuthenticationFrame _instance; + }; +}; +PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT PresenceAuthenticationFrameDefaultTypeInternal _PresenceAuthenticationFrame_default_instance_; +} // namespace presence +} // namespace nearby +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[9]; +static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_presence_2fproto_2fpresence_5fframe_2eproto[2]; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_presence_2fproto_2fpresence_5fframe_2eproto = nullptr; + +const uint32_t TableStruct_presence_2fproto_2fpresence_5fframe_2eproto::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + PROTOBUF_FIELD_OFFSET(::nearby::presence::PresenceFrame, _has_bits_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::PresenceFrame, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::PresenceFrame, v1_frame_), + 0, + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::V1Frame, _internal_metadata_), + ~0u, // no _extensions_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::V1Frame, _oneof_case_[0]), + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + ::PROTOBUF_NAMESPACE_ID::internal::kInvalidFieldOffsetTag, + ::PROTOBUF_NAMESPACE_ID::internal::kInvalidFieldOffsetTag, + ::PROTOBUF_NAMESPACE_ID::internal::kInvalidFieldOffsetTag, + ::PROTOBUF_NAMESPACE_ID::internal::kInvalidFieldOffsetTag, + ::PROTOBUF_NAMESPACE_ID::internal::kInvalidFieldOffsetTag, + ::PROTOBUF_NAMESPACE_ID::internal::kInvalidFieldOffsetTag, + PROTOBUF_FIELD_OFFSET(::nearby::presence::V1Frame, Message_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::DeviceIdentityFrame, _has_bits_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::DeviceIdentityFrame, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::DeviceIdentityFrame, device_name_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::DeviceIdentityFrame, bluetooth_mac_address_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::DeviceIdentityFrame, device_image_url_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::DeviceIdentityFrame, model_id_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::DeviceIdentityFrame, action_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::DeviceIdentityFrame, device_model_name_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::DeviceIdentityFrame, device_type_), + 0, + 1, + 2, + 3, + ~0u, + 4, + 5, + PROTOBUF_FIELD_OFFSET(::nearby::presence::ConnectionInitFrame, _has_bits_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::ConnectionInitFrame, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::ConnectionInitFrame, actions_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::ConnectionInitFrame, identity_type_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::ConnectionInitFrame, uwb_enable_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::ConnectionInitFrame, device_unique_id_), + ~0u, + 0, + 1, + 2, + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, _has_bits_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, controlee_address_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, supported_config_ids_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, supported_channels_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, min_ranging_interval_ms_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, sub_session_id_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, sub_session_key_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, ranging_disabled_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, device_unique_id_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, is_distance_supported_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, is_azimuth_supported_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, is_elevation_supported_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, min_slot_duration_ms_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, supported_ntf_configs_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, is_ranging_interval_reconfigure_supported_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, supported_slot_durations_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, supported_ranging_update_rates_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, chip_count_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbControleeCapabilities, multi_chip_info_), + 0, + ~0u, + ~0u, + 4, + 1, + 2, + 5, + 3, + 9, + 10, + 6, + 11, + ~0u, + 7, + ~0u, + ~0u, + 8, + ~0u, + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbMultiChipInfo, _has_bits_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbMultiChipInfo, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbMultiChipInfo, controlee_address_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbMultiChipInfo, chip_id_), + 0, + 1, + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, _has_bits_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, controller_address_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, channel_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, preamble_index_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, config_id_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, ranging_interval_ms_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, session_id_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, vendor_id_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, static_sts_iv_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, session_key_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::UwbConnectionInfo, ranging_disabled_), + 0, + 4, + 5, + 6, + 7, + 8, + 1, + 2, + 3, + 9, + PROTOBUF_FIELD_OFFSET(::nearby::presence::ControlFrame, _has_bits_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::ControlFrame, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::ControlFrame, type_), + 0, + PROTOBUF_FIELD_OFFSET(::nearby::presence::PresenceAuthenticationFrame, _has_bits_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::PresenceAuthenticationFrame, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + ~0u, // no _inlined_string_donated_ + PROTOBUF_FIELD_OFFSET(::nearby::presence::PresenceAuthenticationFrame, version_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::PresenceAuthenticationFrame, private_key_signature_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::PresenceAuthenticationFrame, shared_credential_id_hash_), + PROTOBUF_FIELD_OFFSET(::nearby::presence::PresenceAuthenticationFrame, credential_id_hash_), + 3, + 0, + 1, + 2, +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, 7, -1, sizeof(::nearby::presence::PresenceFrame)}, + { 8, -1, -1, sizeof(::nearby::presence::V1Frame)}, + { 21, 34, -1, sizeof(::nearby::presence::DeviceIdentityFrame)}, + { 41, 51, -1, sizeof(::nearby::presence::ConnectionInitFrame)}, + { 55, 79, -1, sizeof(::nearby::presence::UwbControleeCapabilities)}, + { 97, 105, -1, sizeof(::nearby::presence::UwbMultiChipInfo)}, + { 107, 123, -1, sizeof(::nearby::presence::UwbConnectionInfo)}, + { 133, 140, -1, sizeof(::nearby::presence::ControlFrame)}, + { 141, 151, -1, sizeof(::nearby::presence::PresenceAuthenticationFrame)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::nearby::presence::_PresenceFrame_default_instance_), + reinterpret_cast(&::nearby::presence::_V1Frame_default_instance_), + reinterpret_cast(&::nearby::presence::_DeviceIdentityFrame_default_instance_), + reinterpret_cast(&::nearby::presence::_ConnectionInitFrame_default_instance_), + reinterpret_cast(&::nearby::presence::_UwbControleeCapabilities_default_instance_), + reinterpret_cast(&::nearby::presence::_UwbMultiChipInfo_default_instance_), + reinterpret_cast(&::nearby::presence::_UwbConnectionInfo_default_instance_), + reinterpret_cast(&::nearby::presence::_ControlFrame_default_instance_), + reinterpret_cast(&::nearby::presence::_PresenceAuthenticationFrame_default_instance_), +}; + +const char descriptor_table_protodef_presence_2fproto_2fpresence_5fframe_2eproto[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n#presence/proto/presence_frame.proto\022\017n" + "earby.presence\"j\n\rPresenceFrame\022*\n\010v1_fr" + "ame\030\001 \001(\0132\030.nearby.presence.V1Frame\"-\n\007V" + "ersion\022\023\n\017UNKNOWN_VERSION\020\000\022\r\n\tVERSION_1" + "\020\001\"\302\003\n\007V1Frame\0226\n\rcontrol_frame\030\001 \001(\0132\035." + "nearby.presence.ControlFrameH\000\022E\n\025device" + "_identity_frame\030\002 \001(\0132$.nearby.presence." + "DeviceIdentityFrameH\000\022E\n\025connection_init" + "_frame\030\003 \001(\0132$.nearby.presence.Connectio" + "nInitFrameH\000\022U\n uwb_controlee_capabiliti" + "es_frame\030\004 \001(\0132).nearby.presence.UwbCont" + "roleeCapabilitiesH\000\022A\n\023uwb_connection_in" + "fo\030\005 \001(\0132\".nearby.presence.UwbConnection" + "InfoH\000\022L\n\024authentication_frame\030\006 \001(\0132,.n" + "earby.presence.PresenceAuthenticationFra" + "meH\000B\t\n\007Message\"\271\001\n\023DeviceIdentityFrame\022" + "\023\n\013device_name\030\001 \001(\t\022\035\n\025bluetooth_mac_ad" + "dress\030\002 \001(\014\022\030\n\020device_image_url\030\003 \001(\t\022\020\n" + "\010model_id\030\004 \001(\t\022\022\n\006action\030\005 \003(\005B\002\020\001\022\031\n\021d" + "evice_model_name\030\006 \001(\t\022\023\n\013device_type\030\007 " + "\001(\005\"o\n\023ConnectionInitFrame\022\023\n\007actions\030\001 " + "\003(\005B\002\020\001\022\025\n\ridentity_type\030\002 \001(\005\022\022\n\nuwb_en" + "able\030\003 \001(\010\022\030\n\020device_unique_id\030\004 \001(\003\"\220\005\n" + "\030UwbControleeCapabilities\022\031\n\021controlee_a" + "ddress\030\001 \001(\014\022 \n\024supported_config_ids\030\002 \003" + "(\005B\002\020\001\022\036\n\022supported_channels\030\003 \003(\005B\002\020\001\022\037" + "\n\027min_ranging_interval_ms\030\004 \001(\005\022\026\n\016sub_s" + "ession_id\030\005 \001(\014\022\027\n\017sub_session_key\030\006 \001(\014" + "\022\030\n\020ranging_disabled\030\007 \001(\010\022\030\n\020device_uni" + "que_id\030\010 \001(\003\022#\n\025is_distance_supported\030\t " + "\001(\010:\004true\022\"\n\024is_azimuth_supported\030\n \001(\010:" + "\004true\022%\n\026is_elevation_supported\030\013 \001(\010:\005f" + "alse\022\037\n\024min_slot_duration_ms\030\014 \001(\002:\0012\022!\n" + "\025supported_ntf_configs\030\r \003(\005B\002\020\001\0228\n)is_r" + "anging_interval_reconfigure_supported\030\016 " + "\001(\010:\005false\022$\n\030supported_slot_durations\030\017" + " \003(\005B\002\020\001\022*\n\036supported_ranging_update_rat" + "es\030\020 \003(\005B\002\020\001\022\025\n\nchip_count\030\021 \001(\005:\0011\022:\n\017m" + "ulti_chip_info\030\022 \003(\0132!.nearby.presence.U" + "wbMultiChipInfo\">\n\020UwbMultiChipInfo\022\031\n\021c" + "ontrolee_address\030\001 \001(\014\022\017\n\007chip_id\030\002 \001(\t\"" + "\365\001\n\021UwbConnectionInfo\022\032\n\022controller_addr" + "ess\030\001 \001(\014\022\017\n\007channel\030\002 \001(\005\022\026\n\016preamble_i" + "ndex\030\003 \001(\005\022\021\n\tconfig_id\030\004 \001(\005\022\033\n\023ranging" + "_interval_ms\030\005 \001(\005\022\022\n\nsession_id\030\006 \001(\005\022\021" + "\n\tvendor_id\030\007 \001(\014\022\025\n\rstatic_sts_iv\030\010 \001(\014" + "\022\023\n\013session_key\030\t \001(\014\022\030\n\020ranging_disable" + "d\030\n \001(\010\"\210\001\n\014ControlFrame\0227\n\004type\030\001 \001(\0162)" + ".nearby.presence.ControlFrame.ControlTyp" + "e\"\?\n\013ControlType\022\020\n\014UNKNOWN_TYPE\020\000\022\016\n\nKE" + "EP_ALIVE\020\001\022\016\n\nDISCONNECT\020\002\"\220\001\n\033PresenceA" + "uthenticationFrame\022\017\n\007version\030\001 \001(\005\022\035\n\025p" + "rivate_key_signature\030\002 \001(\014\022!\n\031shared_cre" + "dential_id_hash\030\003 \001(\014\022\036\n\022credential_id_h" + "ash\030\004 \001(\014B\002\030\001B\?\n&com.google.android.gms." + "nearby.presenceB\025PresenceFrameProtocol" + ; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto = { + false, false, 2238, descriptor_table_protodef_presence_2fproto_2fpresence_5fframe_2eproto, "presence/proto/presence_frame.proto", + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, nullptr, 0, 9, + schemas, file_default_instances, TableStruct_presence_2fproto_2fpresence_5fframe_2eproto::offsets, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto, file_level_enum_descriptors_presence_2fproto_2fpresence_5fframe_2eproto, file_level_service_descriptors_presence_2fproto_2fpresence_5fframe_2eproto, +}; +PROTOBUF_ATTRIBUTE_WEAK const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable* descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter() { + return &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto; +} + +// Force running AddDescriptors() at dynamic initialization time. +PROTOBUF_ATTRIBUTE_INIT_PRIORITY static ::PROTOBUF_NAMESPACE_ID::internal::AddDescriptorsRunner dynamic_init_dummy_presence_2fproto_2fpresence_5fframe_2eproto(&descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto); +namespace nearby { +namespace presence { +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* PresenceFrame_Version_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto); + return file_level_enum_descriptors_presence_2fproto_2fpresence_5fframe_2eproto[0]; +} +bool PresenceFrame_Version_IsValid(int value) { + switch (value) { + case 0: + case 1: + return true; + default: + return false; + } +} + +#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)) +constexpr PresenceFrame_Version PresenceFrame::UNKNOWN_VERSION; +constexpr PresenceFrame_Version PresenceFrame::VERSION_1; +constexpr PresenceFrame_Version PresenceFrame::Version_MIN; +constexpr PresenceFrame_Version PresenceFrame::Version_MAX; +constexpr int PresenceFrame::Version_ARRAYSIZE; +#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)) +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* ControlFrame_ControlType_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto); + return file_level_enum_descriptors_presence_2fproto_2fpresence_5fframe_2eproto[1]; +} +bool ControlFrame_ControlType_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + return true; + default: + return false; + } +} + +#if (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)) +constexpr ControlFrame_ControlType ControlFrame::UNKNOWN_TYPE; +constexpr ControlFrame_ControlType ControlFrame::KEEP_ALIVE; +constexpr ControlFrame_ControlType ControlFrame::DISCONNECT; +constexpr ControlFrame_ControlType ControlFrame::ControlType_MIN; +constexpr ControlFrame_ControlType ControlFrame::ControlType_MAX; +constexpr int ControlFrame::ControlType_ARRAYSIZE; +#endif // (__cplusplus < 201703) && (!defined(_MSC_VER) || (_MSC_VER >= 1900 && _MSC_VER < 1912)) + +// =================================================================== + +class PresenceFrame::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static const ::nearby::presence::V1Frame& v1_frame(const PresenceFrame* msg); + static void set_has_v1_frame(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } +}; + +const ::nearby::presence::V1Frame& +PresenceFrame::_Internal::v1_frame(const PresenceFrame* msg) { + return *msg->v1_frame_; +} +PresenceFrame::PresenceFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:nearby.presence.PresenceFrame) +} +PresenceFrame::PresenceFrame(const PresenceFrame& from) + : ::PROTOBUF_NAMESPACE_ID::Message(), + _has_bits_(from._has_bits_) { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + if (from._internal_has_v1_frame()) { + v1_frame_ = new ::nearby::presence::V1Frame(*from.v1_frame_); + } else { + v1_frame_ = nullptr; + } + // @@protoc_insertion_point(copy_constructor:nearby.presence.PresenceFrame) +} + +inline void PresenceFrame::SharedCtor() { +v1_frame_ = nullptr; +} + +PresenceFrame::~PresenceFrame() { + // @@protoc_insertion_point(destructor:nearby.presence.PresenceFrame) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +inline void PresenceFrame::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + if (this != internal_default_instance()) delete v1_frame_; +} + +void PresenceFrame::ArenaDtor(void* object) { + PresenceFrame* _this = reinterpret_cast< PresenceFrame* >(object); + (void)_this; +} +void PresenceFrame::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void PresenceFrame::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void PresenceFrame::Clear() { +// @@protoc_insertion_point(message_clear_start:nearby.presence.PresenceFrame) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000001u) { + GOOGLE_DCHECK(v1_frame_ != nullptr); + v1_frame_->Clear(); + } + _has_bits_.Clear(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PresenceFrame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // optional .nearby.presence.V1Frame v1_frame = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + ptr = ctx->ParseMessage(_internal_mutable_v1_frame(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PresenceFrame::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:nearby.presence.PresenceFrame) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + // optional .nearby.presence.V1Frame v1_frame = 1; + if (cached_has_bits & 0x00000001u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 1, _Internal::v1_frame(this), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:nearby.presence.PresenceFrame) + return target; +} + +size_t PresenceFrame::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:nearby.presence.PresenceFrame) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // optional .nearby.presence.V1Frame v1_frame = 1; + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000001u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *v1_frame_); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PresenceFrame::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, + PresenceFrame::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PresenceFrame::GetClassData() const { return &_class_data_; } + +void PresenceFrame::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, + const ::PROTOBUF_NAMESPACE_ID::Message& from) { + static_cast(to)->MergeFrom( + static_cast(from)); +} + + +void PresenceFrame::MergeFrom(const PresenceFrame& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:nearby.presence.PresenceFrame) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (from._internal_has_v1_frame()) { + _internal_mutable_v1_frame()->::nearby::presence::V1Frame::MergeFrom(from._internal_v1_frame()); + } + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PresenceFrame::CopyFrom(const PresenceFrame& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:nearby.presence.PresenceFrame) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PresenceFrame::IsInitialized() const { + return true; +} + +void PresenceFrame::InternalSwap(PresenceFrame* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + swap(v1_frame_, other->v1_frame_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PresenceFrame::GetMetadata() const { + return ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors( + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter, &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[0]); +} + +// =================================================================== + +class V1Frame::_Internal { + public: + static const ::nearby::presence::ControlFrame& control_frame(const V1Frame* msg); + static const ::nearby::presence::DeviceIdentityFrame& device_identity_frame(const V1Frame* msg); + static const ::nearby::presence::ConnectionInitFrame& connection_init_frame(const V1Frame* msg); + static const ::nearby::presence::UwbControleeCapabilities& uwb_controlee_capabilities_frame(const V1Frame* msg); + static const ::nearby::presence::UwbConnectionInfo& uwb_connection_info(const V1Frame* msg); + static const ::nearby::presence::PresenceAuthenticationFrame& authentication_frame(const V1Frame* msg); +}; + +const ::nearby::presence::ControlFrame& +V1Frame::_Internal::control_frame(const V1Frame* msg) { + return *msg->Message_.control_frame_; +} +const ::nearby::presence::DeviceIdentityFrame& +V1Frame::_Internal::device_identity_frame(const V1Frame* msg) { + return *msg->Message_.device_identity_frame_; +} +const ::nearby::presence::ConnectionInitFrame& +V1Frame::_Internal::connection_init_frame(const V1Frame* msg) { + return *msg->Message_.connection_init_frame_; +} +const ::nearby::presence::UwbControleeCapabilities& +V1Frame::_Internal::uwb_controlee_capabilities_frame(const V1Frame* msg) { + return *msg->Message_.uwb_controlee_capabilities_frame_; +} +const ::nearby::presence::UwbConnectionInfo& +V1Frame::_Internal::uwb_connection_info(const V1Frame* msg) { + return *msg->Message_.uwb_connection_info_; +} +const ::nearby::presence::PresenceAuthenticationFrame& +V1Frame::_Internal::authentication_frame(const V1Frame* msg) { + return *msg->Message_.authentication_frame_; +} +void V1Frame::set_allocated_control_frame(::nearby::presence::ControlFrame* control_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_Message(); + if (control_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper<::nearby::presence::ControlFrame>::GetOwningArena(control_frame); + if (message_arena != submessage_arena) { + control_frame = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, control_frame, submessage_arena); + } + set_has_control_frame(); + Message_.control_frame_ = control_frame; + } + // @@protoc_insertion_point(field_set_allocated:nearby.presence.V1Frame.control_frame) +} +void V1Frame::set_allocated_device_identity_frame(::nearby::presence::DeviceIdentityFrame* device_identity_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_Message(); + if (device_identity_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper<::nearby::presence::DeviceIdentityFrame>::GetOwningArena(device_identity_frame); + if (message_arena != submessage_arena) { + device_identity_frame = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, device_identity_frame, submessage_arena); + } + set_has_device_identity_frame(); + Message_.device_identity_frame_ = device_identity_frame; + } + // @@protoc_insertion_point(field_set_allocated:nearby.presence.V1Frame.device_identity_frame) +} +void V1Frame::set_allocated_connection_init_frame(::nearby::presence::ConnectionInitFrame* connection_init_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_Message(); + if (connection_init_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper<::nearby::presence::ConnectionInitFrame>::GetOwningArena(connection_init_frame); + if (message_arena != submessage_arena) { + connection_init_frame = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, connection_init_frame, submessage_arena); + } + set_has_connection_init_frame(); + Message_.connection_init_frame_ = connection_init_frame; + } + // @@protoc_insertion_point(field_set_allocated:nearby.presence.V1Frame.connection_init_frame) +} +void V1Frame::set_allocated_uwb_controlee_capabilities_frame(::nearby::presence::UwbControleeCapabilities* uwb_controlee_capabilities_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_Message(); + if (uwb_controlee_capabilities_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper<::nearby::presence::UwbControleeCapabilities>::GetOwningArena(uwb_controlee_capabilities_frame); + if (message_arena != submessage_arena) { + uwb_controlee_capabilities_frame = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, uwb_controlee_capabilities_frame, submessage_arena); + } + set_has_uwb_controlee_capabilities_frame(); + Message_.uwb_controlee_capabilities_frame_ = uwb_controlee_capabilities_frame; + } + // @@protoc_insertion_point(field_set_allocated:nearby.presence.V1Frame.uwb_controlee_capabilities_frame) +} +void V1Frame::set_allocated_uwb_connection_info(::nearby::presence::UwbConnectionInfo* uwb_connection_info) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_Message(); + if (uwb_connection_info) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper<::nearby::presence::UwbConnectionInfo>::GetOwningArena(uwb_connection_info); + if (message_arena != submessage_arena) { + uwb_connection_info = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, uwb_connection_info, submessage_arena); + } + set_has_uwb_connection_info(); + Message_.uwb_connection_info_ = uwb_connection_info; + } + // @@protoc_insertion_point(field_set_allocated:nearby.presence.V1Frame.uwb_connection_info) +} +void V1Frame::set_allocated_authentication_frame(::nearby::presence::PresenceAuthenticationFrame* authentication_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + clear_Message(); + if (authentication_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper<::nearby::presence::PresenceAuthenticationFrame>::GetOwningArena(authentication_frame); + if (message_arena != submessage_arena) { + authentication_frame = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, authentication_frame, submessage_arena); + } + set_has_authentication_frame(); + Message_.authentication_frame_ = authentication_frame; + } + // @@protoc_insertion_point(field_set_allocated:nearby.presence.V1Frame.authentication_frame) +} +V1Frame::V1Frame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:nearby.presence.V1Frame) +} +V1Frame::V1Frame(const V1Frame& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + clear_has_Message(); + switch (from.Message_case()) { + case kControlFrame: { + _internal_mutable_control_frame()->::nearby::presence::ControlFrame::MergeFrom(from._internal_control_frame()); + break; + } + case kDeviceIdentityFrame: { + _internal_mutable_device_identity_frame()->::nearby::presence::DeviceIdentityFrame::MergeFrom(from._internal_device_identity_frame()); + break; + } + case kConnectionInitFrame: { + _internal_mutable_connection_init_frame()->::nearby::presence::ConnectionInitFrame::MergeFrom(from._internal_connection_init_frame()); + break; + } + case kUwbControleeCapabilitiesFrame: { + _internal_mutable_uwb_controlee_capabilities_frame()->::nearby::presence::UwbControleeCapabilities::MergeFrom(from._internal_uwb_controlee_capabilities_frame()); + break; + } + case kUwbConnectionInfo: { + _internal_mutable_uwb_connection_info()->::nearby::presence::UwbConnectionInfo::MergeFrom(from._internal_uwb_connection_info()); + break; + } + case kAuthenticationFrame: { + _internal_mutable_authentication_frame()->::nearby::presence::PresenceAuthenticationFrame::MergeFrom(from._internal_authentication_frame()); + break; + } + case MESSAGE_NOT_SET: { + break; + } + } + // @@protoc_insertion_point(copy_constructor:nearby.presence.V1Frame) +} + +inline void V1Frame::SharedCtor() { +clear_has_Message(); +} + +V1Frame::~V1Frame() { + // @@protoc_insertion_point(destructor:nearby.presence.V1Frame) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +inline void V1Frame::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + if (has_Message()) { + clear_Message(); + } +} + +void V1Frame::ArenaDtor(void* object) { + V1Frame* _this = reinterpret_cast< V1Frame* >(object); + (void)_this; +} +void V1Frame::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void V1Frame::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void V1Frame::clear_Message() { +// @@protoc_insertion_point(one_of_clear_start:nearby.presence.V1Frame) + switch (Message_case()) { + case kControlFrame: { + if (GetArenaForAllocation() == nullptr) { + delete Message_.control_frame_; + } + break; + } + case kDeviceIdentityFrame: { + if (GetArenaForAllocation() == nullptr) { + delete Message_.device_identity_frame_; + } + break; + } + case kConnectionInitFrame: { + if (GetArenaForAllocation() == nullptr) { + delete Message_.connection_init_frame_; + } + break; + } + case kUwbControleeCapabilitiesFrame: { + if (GetArenaForAllocation() == nullptr) { + delete Message_.uwb_controlee_capabilities_frame_; + } + break; + } + case kUwbConnectionInfo: { + if (GetArenaForAllocation() == nullptr) { + delete Message_.uwb_connection_info_; + } + break; + } + case kAuthenticationFrame: { + if (GetArenaForAllocation() == nullptr) { + delete Message_.authentication_frame_; + } + break; + } + case MESSAGE_NOT_SET: { + break; + } + } + _oneof_case_[0] = MESSAGE_NOT_SET; +} + + +void V1Frame::Clear() { +// @@protoc_insertion_point(message_clear_start:nearby.presence.V1Frame) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + clear_Message(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* V1Frame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // .nearby.presence.ControlFrame control_frame = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + ptr = ctx->ParseMessage(_internal_mutable_control_frame(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .nearby.presence.DeviceIdentityFrame device_identity_frame = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + ptr = ctx->ParseMessage(_internal_mutable_device_identity_frame(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .nearby.presence.ConnectionInitFrame connection_init_frame = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ctx->ParseMessage(_internal_mutable_connection_init_frame(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .nearby.presence.UwbControleeCapabilities uwb_controlee_capabilities_frame = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + ptr = ctx->ParseMessage(_internal_mutable_uwb_controlee_capabilities_frame(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .nearby.presence.UwbConnectionInfo uwb_connection_info = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + ptr = ctx->ParseMessage(_internal_mutable_uwb_connection_info(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // .nearby.presence.PresenceAuthenticationFrame authentication_frame = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 50)) { + ptr = ctx->ParseMessage(_internal_mutable_authentication_frame(), ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* V1Frame::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:nearby.presence.V1Frame) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + switch (Message_case()) { + case kControlFrame: { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 1, _Internal::control_frame(this), target, stream); + break; + } + case kDeviceIdentityFrame: { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 2, _Internal::device_identity_frame(this), target, stream); + break; + } + case kConnectionInitFrame: { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 3, _Internal::connection_init_frame(this), target, stream); + break; + } + case kUwbControleeCapabilitiesFrame: { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 4, _Internal::uwb_controlee_capabilities_frame(this), target, stream); + break; + } + case kUwbConnectionInfo: { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 5, _Internal::uwb_connection_info(this), target, stream); + break; + } + case kAuthenticationFrame: { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage( + 6, _Internal::authentication_frame(this), target, stream); + break; + } + default: ; + } + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:nearby.presence.V1Frame) + return target; +} + +size_t V1Frame::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:nearby.presence.V1Frame) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + switch (Message_case()) { + // .nearby.presence.ControlFrame control_frame = 1; + case kControlFrame: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *Message_.control_frame_); + break; + } + // .nearby.presence.DeviceIdentityFrame device_identity_frame = 2; + case kDeviceIdentityFrame: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *Message_.device_identity_frame_); + break; + } + // .nearby.presence.ConnectionInitFrame connection_init_frame = 3; + case kConnectionInitFrame: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *Message_.connection_init_frame_); + break; + } + // .nearby.presence.UwbControleeCapabilities uwb_controlee_capabilities_frame = 4; + case kUwbControleeCapabilitiesFrame: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *Message_.uwb_controlee_capabilities_frame_); + break; + } + // .nearby.presence.UwbConnectionInfo uwb_connection_info = 5; + case kUwbConnectionInfo: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *Message_.uwb_connection_info_); + break; + } + // .nearby.presence.PresenceAuthenticationFrame authentication_frame = 6; + case kAuthenticationFrame: { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize( + *Message_.authentication_frame_); + break; + } + case MESSAGE_NOT_SET: { + break; + } + } + return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData V1Frame::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, + V1Frame::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*V1Frame::GetClassData() const { return &_class_data_; } + +void V1Frame::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, + const ::PROTOBUF_NAMESPACE_ID::Message& from) { + static_cast(to)->MergeFrom( + static_cast(from)); +} + + +void V1Frame::MergeFrom(const V1Frame& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:nearby.presence.V1Frame) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + switch (from.Message_case()) { + case kControlFrame: { + _internal_mutable_control_frame()->::nearby::presence::ControlFrame::MergeFrom(from._internal_control_frame()); + break; + } + case kDeviceIdentityFrame: { + _internal_mutable_device_identity_frame()->::nearby::presence::DeviceIdentityFrame::MergeFrom(from._internal_device_identity_frame()); + break; + } + case kConnectionInitFrame: { + _internal_mutable_connection_init_frame()->::nearby::presence::ConnectionInitFrame::MergeFrom(from._internal_connection_init_frame()); + break; + } + case kUwbControleeCapabilitiesFrame: { + _internal_mutable_uwb_controlee_capabilities_frame()->::nearby::presence::UwbControleeCapabilities::MergeFrom(from._internal_uwb_controlee_capabilities_frame()); + break; + } + case kUwbConnectionInfo: { + _internal_mutable_uwb_connection_info()->::nearby::presence::UwbConnectionInfo::MergeFrom(from._internal_uwb_connection_info()); + break; + } + case kAuthenticationFrame: { + _internal_mutable_authentication_frame()->::nearby::presence::PresenceAuthenticationFrame::MergeFrom(from._internal_authentication_frame()); + break; + } + case MESSAGE_NOT_SET: { + break; + } + } + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void V1Frame::CopyFrom(const V1Frame& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:nearby.presence.V1Frame) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool V1Frame::IsInitialized() const { + return true; +} + +void V1Frame::InternalSwap(V1Frame* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(Message_, other->Message_); + swap(_oneof_case_[0], other->_oneof_case_[0]); +} + +::PROTOBUF_NAMESPACE_ID::Metadata V1Frame::GetMetadata() const { + return ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors( + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter, &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[1]); +} + +// =================================================================== + +class DeviceIdentityFrame::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static void set_has_device_name(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } + static void set_has_bluetooth_mac_address(HasBits* has_bits) { + (*has_bits)[0] |= 2u; + } + static void set_has_device_image_url(HasBits* has_bits) { + (*has_bits)[0] |= 4u; + } + static void set_has_model_id(HasBits* has_bits) { + (*has_bits)[0] |= 8u; + } + static void set_has_device_model_name(HasBits* has_bits) { + (*has_bits)[0] |= 16u; + } + static void set_has_device_type(HasBits* has_bits) { + (*has_bits)[0] |= 32u; + } +}; + +DeviceIdentityFrame::DeviceIdentityFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned), + action_(arena) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:nearby.presence.DeviceIdentityFrame) +} +DeviceIdentityFrame::DeviceIdentityFrame(const DeviceIdentityFrame& from) + : ::PROTOBUF_NAMESPACE_ID::Message(), + _has_bits_(from._has_bits_), + action_(from.action_) { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + device_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + device_name_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_device_name()) { + device_name_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_device_name(), + GetArenaForAllocation()); + } + bluetooth_mac_address_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + bluetooth_mac_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_bluetooth_mac_address()) { + bluetooth_mac_address_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_bluetooth_mac_address(), + GetArenaForAllocation()); + } + device_image_url_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + device_image_url_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_device_image_url()) { + device_image_url_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_device_image_url(), + GetArenaForAllocation()); + } + model_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + model_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_model_id()) { + model_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_model_id(), + GetArenaForAllocation()); + } + device_model_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + device_model_name_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_device_model_name()) { + device_model_name_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_device_model_name(), + GetArenaForAllocation()); + } + device_type_ = from.device_type_; + // @@protoc_insertion_point(copy_constructor:nearby.presence.DeviceIdentityFrame) +} + +inline void DeviceIdentityFrame::SharedCtor() { +device_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + device_name_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +bluetooth_mac_address_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + bluetooth_mac_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +device_image_url_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + device_image_url_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +model_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + model_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +device_model_name_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + device_model_name_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +device_type_ = 0; +} + +DeviceIdentityFrame::~DeviceIdentityFrame() { + // @@protoc_insertion_point(destructor:nearby.presence.DeviceIdentityFrame) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +inline void DeviceIdentityFrame::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + device_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + bluetooth_mac_address_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + device_image_url_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + model_id_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + device_model_name_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void DeviceIdentityFrame::ArenaDtor(void* object) { + DeviceIdentityFrame* _this = reinterpret_cast< DeviceIdentityFrame* >(object); + (void)_this; +} +void DeviceIdentityFrame::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void DeviceIdentityFrame::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void DeviceIdentityFrame::Clear() { +// @@protoc_insertion_point(message_clear_start:nearby.presence.DeviceIdentityFrame) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + action_.Clear(); + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x0000001fu) { + if (cached_has_bits & 0x00000001u) { + device_name_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000002u) { + bluetooth_mac_address_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000004u) { + device_image_url_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000008u) { + model_id_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000010u) { + device_model_name_.ClearNonDefaultToEmpty(); + } + } + device_type_ = 0; + _has_bits_.Clear(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* DeviceIdentityFrame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // optional string device_name = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_device_name(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + #ifndef NDEBUG + ::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "nearby.presence.DeviceIdentityFrame.device_name"); + #endif // !NDEBUG + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bytes bluetooth_mac_address = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_bluetooth_mac_address(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional string device_image_url = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_device_image_url(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + #ifndef NDEBUG + ::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "nearby.presence.DeviceIdentityFrame.device_image_url"); + #endif // !NDEBUG + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional string model_id = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_model_id(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + #ifndef NDEBUG + ::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "nearby.presence.DeviceIdentityFrame.model_id"); + #endif // !NDEBUG + CHK_(ptr); + } else + goto handle_unusual; + continue; + // repeated int32 action = 5 [packed = true]; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_action(), ptr, ctx); + CHK_(ptr); + } else if (static_cast(tag) == 40) { + _internal_add_action(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr)); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional string device_model_name = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 50)) { + auto str = _internal_mutable_device_model_name(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + #ifndef NDEBUG + ::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "nearby.presence.DeviceIdentityFrame.device_model_name"); + #endif // !NDEBUG + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 device_type = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 56)) { + _Internal::set_has_device_type(&has_bits); + device_type_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* DeviceIdentityFrame::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:nearby.presence.DeviceIdentityFrame) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + // optional string device_name = 1; + if (cached_has_bits & 0x00000001u) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField( + this->_internal_device_name().data(), static_cast(this->_internal_device_name().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE, + "nearby.presence.DeviceIdentityFrame.device_name"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_device_name(), target); + } + + // optional bytes bluetooth_mac_address = 2; + if (cached_has_bits & 0x00000002u) { + target = stream->WriteBytesMaybeAliased( + 2, this->_internal_bluetooth_mac_address(), target); + } + + // optional string device_image_url = 3; + if (cached_has_bits & 0x00000004u) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField( + this->_internal_device_image_url().data(), static_cast(this->_internal_device_image_url().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE, + "nearby.presence.DeviceIdentityFrame.device_image_url"); + target = stream->WriteStringMaybeAliased( + 3, this->_internal_device_image_url(), target); + } + + // optional string model_id = 4; + if (cached_has_bits & 0x00000008u) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField( + this->_internal_model_id().data(), static_cast(this->_internal_model_id().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE, + "nearby.presence.DeviceIdentityFrame.model_id"); + target = stream->WriteStringMaybeAliased( + 4, this->_internal_model_id(), target); + } + + // repeated int32 action = 5 [packed = true]; + { + int byte_size = _action_cached_byte_size_.load(std::memory_order_relaxed); + if (byte_size > 0) { + target = stream->WriteInt32Packed( + 5, _internal_action(), byte_size, target); + } + } + + // optional string device_model_name = 6; + if (cached_has_bits & 0x00000010u) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField( + this->_internal_device_model_name().data(), static_cast(this->_internal_device_model_name().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE, + "nearby.presence.DeviceIdentityFrame.device_model_name"); + target = stream->WriteStringMaybeAliased( + 6, this->_internal_device_model_name(), target); + } + + // optional int32 device_type = 7; + if (cached_has_bits & 0x00000020u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(7, this->_internal_device_type(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:nearby.presence.DeviceIdentityFrame) + return target; +} + +size_t DeviceIdentityFrame::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:nearby.presence.DeviceIdentityFrame) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated int32 action = 5 [packed = true]; + { + size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + Int32Size(this->action_); + if (data_size > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( + static_cast(data_size)); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); + _action_cached_byte_size_.store(cached_size, + std::memory_order_relaxed); + total_size += data_size; + } + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x0000003fu) { + // optional string device_name = 1; + if (cached_has_bits & 0x00000001u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_device_name()); + } + + // optional bytes bluetooth_mac_address = 2; + if (cached_has_bits & 0x00000002u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_bluetooth_mac_address()); + } + + // optional string device_image_url = 3; + if (cached_has_bits & 0x00000004u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_device_image_url()); + } + + // optional string model_id = 4; + if (cached_has_bits & 0x00000008u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_model_id()); + } + + // optional string device_model_name = 6; + if (cached_has_bits & 0x00000010u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_device_model_name()); + } + + // optional int32 device_type = 7; + if (cached_has_bits & 0x00000020u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_device_type()); + } + + } + return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData DeviceIdentityFrame::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, + DeviceIdentityFrame::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*DeviceIdentityFrame::GetClassData() const { return &_class_data_; } + +void DeviceIdentityFrame::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, + const ::PROTOBUF_NAMESPACE_ID::Message& from) { + static_cast(to)->MergeFrom( + static_cast(from)); +} + + +void DeviceIdentityFrame::MergeFrom(const DeviceIdentityFrame& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:nearby.presence.DeviceIdentityFrame) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + action_.MergeFrom(from.action_); + cached_has_bits = from._has_bits_[0]; + if (cached_has_bits & 0x0000003fu) { + if (cached_has_bits & 0x00000001u) { + _internal_set_device_name(from._internal_device_name()); + } + if (cached_has_bits & 0x00000002u) { + _internal_set_bluetooth_mac_address(from._internal_bluetooth_mac_address()); + } + if (cached_has_bits & 0x00000004u) { + _internal_set_device_image_url(from._internal_device_image_url()); + } + if (cached_has_bits & 0x00000008u) { + _internal_set_model_id(from._internal_model_id()); + } + if (cached_has_bits & 0x00000010u) { + _internal_set_device_model_name(from._internal_device_model_name()); + } + if (cached_has_bits & 0x00000020u) { + device_type_ = from.device_type_; + } + _has_bits_[0] |= cached_has_bits; + } + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void DeviceIdentityFrame::CopyFrom(const DeviceIdentityFrame& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:nearby.presence.DeviceIdentityFrame) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool DeviceIdentityFrame::IsInitialized() const { + return true; +} + +void DeviceIdentityFrame::InternalSwap(DeviceIdentityFrame* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + action_.InternalSwap(&other->action_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &device_name_, lhs_arena, + &other->device_name_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &bluetooth_mac_address_, lhs_arena, + &other->bluetooth_mac_address_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &device_image_url_, lhs_arena, + &other->device_image_url_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &model_id_, lhs_arena, + &other->model_id_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &device_model_name_, lhs_arena, + &other->device_model_name_, rhs_arena + ); + swap(device_type_, other->device_type_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata DeviceIdentityFrame::GetMetadata() const { + return ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors( + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter, &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[2]); +} + +// =================================================================== + +class ConnectionInitFrame::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static void set_has_identity_type(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } + static void set_has_uwb_enable(HasBits* has_bits) { + (*has_bits)[0] |= 2u; + } + static void set_has_device_unique_id(HasBits* has_bits) { + (*has_bits)[0] |= 4u; + } +}; + +ConnectionInitFrame::ConnectionInitFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned), + actions_(arena) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:nearby.presence.ConnectionInitFrame) +} +ConnectionInitFrame::ConnectionInitFrame(const ConnectionInitFrame& from) + : ::PROTOBUF_NAMESPACE_ID::Message(), + _has_bits_(from._has_bits_), + actions_(from.actions_) { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::memcpy(&identity_type_, &from.identity_type_, + static_cast(reinterpret_cast(&device_unique_id_) - + reinterpret_cast(&identity_type_)) + sizeof(device_unique_id_)); + // @@protoc_insertion_point(copy_constructor:nearby.presence.ConnectionInitFrame) +} + +inline void ConnectionInitFrame::SharedCtor() { +::memset(reinterpret_cast(this) + static_cast( + reinterpret_cast(&identity_type_) - reinterpret_cast(this)), + 0, static_cast(reinterpret_cast(&device_unique_id_) - + reinterpret_cast(&identity_type_)) + sizeof(device_unique_id_)); +} + +ConnectionInitFrame::~ConnectionInitFrame() { + // @@protoc_insertion_point(destructor:nearby.presence.ConnectionInitFrame) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +inline void ConnectionInitFrame::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); +} + +void ConnectionInitFrame::ArenaDtor(void* object) { + ConnectionInitFrame* _this = reinterpret_cast< ConnectionInitFrame* >(object); + (void)_this; +} +void ConnectionInitFrame::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void ConnectionInitFrame::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void ConnectionInitFrame::Clear() { +// @@protoc_insertion_point(message_clear_start:nearby.presence.ConnectionInitFrame) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + actions_.Clear(); + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000007u) { + ::memset(&identity_type_, 0, static_cast( + reinterpret_cast(&device_unique_id_) - + reinterpret_cast(&identity_type_)) + sizeof(device_unique_id_)); + } + _has_bits_.Clear(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* ConnectionInitFrame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // repeated int32 actions = 1 [packed = true]; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_actions(), ptr, ctx); + CHK_(ptr); + } else if (static_cast(tag) == 8) { + _internal_add_actions(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr)); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 identity_type = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 16)) { + _Internal::set_has_identity_type(&has_bits); + identity_type_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bool uwb_enable = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 24)) { + _Internal::set_has_uwb_enable(&has_bits); + uwb_enable_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int64 device_unique_id = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 32)) { + _Internal::set_has_device_unique_id(&has_bits); + device_unique_id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* ConnectionInitFrame::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:nearby.presence.ConnectionInitFrame) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + // repeated int32 actions = 1 [packed = true]; + { + int byte_size = _actions_cached_byte_size_.load(std::memory_order_relaxed); + if (byte_size > 0) { + target = stream->WriteInt32Packed( + 1, _internal_actions(), byte_size, target); + } + } + + cached_has_bits = _has_bits_[0]; + // optional int32 identity_type = 2; + if (cached_has_bits & 0x00000001u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(2, this->_internal_identity_type(), target); + } + + // optional bool uwb_enable = 3; + if (cached_has_bits & 0x00000002u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(3, this->_internal_uwb_enable(), target); + } + + // optional int64 device_unique_id = 4; + if (cached_has_bits & 0x00000004u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(4, this->_internal_device_unique_id(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:nearby.presence.ConnectionInitFrame) + return target; +} + +size_t ConnectionInitFrame::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:nearby.presence.ConnectionInitFrame) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated int32 actions = 1 [packed = true]; + { + size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + Int32Size(this->actions_); + if (data_size > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( + static_cast(data_size)); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); + _actions_cached_byte_size_.store(cached_size, + std::memory_order_relaxed); + total_size += data_size; + } + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000007u) { + // optional int32 identity_type = 2; + if (cached_has_bits & 0x00000001u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_identity_type()); + } + + // optional bool uwb_enable = 3; + if (cached_has_bits & 0x00000002u) { + total_size += 1 + 1; + } + + // optional int64 device_unique_id = 4; + if (cached_has_bits & 0x00000004u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64SizePlusOne(this->_internal_device_unique_id()); + } + + } + return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ConnectionInitFrame::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, + ConnectionInitFrame::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ConnectionInitFrame::GetClassData() const { return &_class_data_; } + +void ConnectionInitFrame::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, + const ::PROTOBUF_NAMESPACE_ID::Message& from) { + static_cast(to)->MergeFrom( + static_cast(from)); +} + + +void ConnectionInitFrame::MergeFrom(const ConnectionInitFrame& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:nearby.presence.ConnectionInitFrame) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + actions_.MergeFrom(from.actions_); + cached_has_bits = from._has_bits_[0]; + if (cached_has_bits & 0x00000007u) { + if (cached_has_bits & 0x00000001u) { + identity_type_ = from.identity_type_; + } + if (cached_has_bits & 0x00000002u) { + uwb_enable_ = from.uwb_enable_; + } + if (cached_has_bits & 0x00000004u) { + device_unique_id_ = from.device_unique_id_; + } + _has_bits_[0] |= cached_has_bits; + } + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void ConnectionInitFrame::CopyFrom(const ConnectionInitFrame& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:nearby.presence.ConnectionInitFrame) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ConnectionInitFrame::IsInitialized() const { + return true; +} + +void ConnectionInitFrame::InternalSwap(ConnectionInitFrame* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + actions_.InternalSwap(&other->actions_); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(ConnectionInitFrame, device_unique_id_) + + sizeof(ConnectionInitFrame::device_unique_id_) + - PROTOBUF_FIELD_OFFSET(ConnectionInitFrame, identity_type_)>( + reinterpret_cast(&identity_type_), + reinterpret_cast(&other->identity_type_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata ConnectionInitFrame::GetMetadata() const { + return ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors( + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter, &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[3]); +} + +// =================================================================== + +class UwbControleeCapabilities::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static void set_has_controlee_address(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } + static void set_has_min_ranging_interval_ms(HasBits* has_bits) { + (*has_bits)[0] |= 16u; + } + static void set_has_sub_session_id(HasBits* has_bits) { + (*has_bits)[0] |= 2u; + } + static void set_has_sub_session_key(HasBits* has_bits) { + (*has_bits)[0] |= 4u; + } + static void set_has_ranging_disabled(HasBits* has_bits) { + (*has_bits)[0] |= 32u; + } + static void set_has_device_unique_id(HasBits* has_bits) { + (*has_bits)[0] |= 8u; + } + static void set_has_is_distance_supported(HasBits* has_bits) { + (*has_bits)[0] |= 512u; + } + static void set_has_is_azimuth_supported(HasBits* has_bits) { + (*has_bits)[0] |= 1024u; + } + static void set_has_is_elevation_supported(HasBits* has_bits) { + (*has_bits)[0] |= 64u; + } + static void set_has_min_slot_duration_ms(HasBits* has_bits) { + (*has_bits)[0] |= 2048u; + } + static void set_has_is_ranging_interval_reconfigure_supported(HasBits* has_bits) { + (*has_bits)[0] |= 128u; + } + static void set_has_chip_count(HasBits* has_bits) { + (*has_bits)[0] |= 256u; + } +}; + +UwbControleeCapabilities::UwbControleeCapabilities(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned), + supported_config_ids_(arena), + supported_channels_(arena), + supported_ntf_configs_(arena), + supported_slot_durations_(arena), + supported_ranging_update_rates_(arena), + multi_chip_info_(arena) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:nearby.presence.UwbControleeCapabilities) +} +UwbControleeCapabilities::UwbControleeCapabilities(const UwbControleeCapabilities& from) + : ::PROTOBUF_NAMESPACE_ID::Message(), + _has_bits_(from._has_bits_), + supported_config_ids_(from.supported_config_ids_), + supported_channels_(from.supported_channels_), + supported_ntf_configs_(from.supported_ntf_configs_), + supported_slot_durations_(from.supported_slot_durations_), + supported_ranging_update_rates_(from.supported_ranging_update_rates_), + multi_chip_info_(from.multi_chip_info_) { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + controlee_address_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + controlee_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_controlee_address()) { + controlee_address_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_controlee_address(), + GetArenaForAllocation()); + } + sub_session_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + sub_session_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_sub_session_id()) { + sub_session_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_sub_session_id(), + GetArenaForAllocation()); + } + sub_session_key_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + sub_session_key_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_sub_session_key()) { + sub_session_key_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_sub_session_key(), + GetArenaForAllocation()); + } + ::memcpy(&device_unique_id_, &from.device_unique_id_, + static_cast(reinterpret_cast(&min_slot_duration_ms_) - + reinterpret_cast(&device_unique_id_)) + sizeof(min_slot_duration_ms_)); + // @@protoc_insertion_point(copy_constructor:nearby.presence.UwbControleeCapabilities) +} + +inline void UwbControleeCapabilities::SharedCtor() { +controlee_address_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + controlee_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +sub_session_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + sub_session_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +sub_session_key_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + sub_session_key_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +::memset(reinterpret_cast(this) + static_cast( + reinterpret_cast(&device_unique_id_) - reinterpret_cast(this)), + 0, static_cast(reinterpret_cast(&is_ranging_interval_reconfigure_supported_) - + reinterpret_cast(&device_unique_id_)) + sizeof(is_ranging_interval_reconfigure_supported_)); +chip_count_ = 1; +is_distance_supported_ = true; +is_azimuth_supported_ = true; +min_slot_duration_ms_ = 2; +} + +UwbControleeCapabilities::~UwbControleeCapabilities() { + // @@protoc_insertion_point(destructor:nearby.presence.UwbControleeCapabilities) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +inline void UwbControleeCapabilities::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + controlee_address_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + sub_session_id_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + sub_session_key_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void UwbControleeCapabilities::ArenaDtor(void* object) { + UwbControleeCapabilities* _this = reinterpret_cast< UwbControleeCapabilities* >(object); + (void)_this; +} +void UwbControleeCapabilities::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void UwbControleeCapabilities::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void UwbControleeCapabilities::Clear() { +// @@protoc_insertion_point(message_clear_start:nearby.presence.UwbControleeCapabilities) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + supported_config_ids_.Clear(); + supported_channels_.Clear(); + supported_ntf_configs_.Clear(); + supported_slot_durations_.Clear(); + supported_ranging_update_rates_.Clear(); + multi_chip_info_.Clear(); + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000007u) { + if (cached_has_bits & 0x00000001u) { + controlee_address_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000002u) { + sub_session_id_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000004u) { + sub_session_key_.ClearNonDefaultToEmpty(); + } + } + if (cached_has_bits & 0x000000f8u) { + ::memset(&device_unique_id_, 0, static_cast( + reinterpret_cast(&is_ranging_interval_reconfigure_supported_) - + reinterpret_cast(&device_unique_id_)) + sizeof(is_ranging_interval_reconfigure_supported_)); + } + if (cached_has_bits & 0x00000f00u) { + chip_count_ = 1; + is_distance_supported_ = true; + is_azimuth_supported_ = true; + min_slot_duration_ms_ = 2; + } + _has_bits_.Clear(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* UwbControleeCapabilities::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // optional bytes controlee_address = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_controlee_address(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // repeated int32 supported_config_ids = 2 [packed = true]; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_supported_config_ids(), ptr, ctx); + CHK_(ptr); + } else if (static_cast(tag) == 16) { + _internal_add_supported_config_ids(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr)); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // repeated int32 supported_channels = 3 [packed = true]; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_supported_channels(), ptr, ctx); + CHK_(ptr); + } else if (static_cast(tag) == 24) { + _internal_add_supported_channels(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr)); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 min_ranging_interval_ms = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 32)) { + _Internal::set_has_min_ranging_interval_ms(&has_bits); + min_ranging_interval_ms_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bytes sub_session_id = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 42)) { + auto str = _internal_mutable_sub_session_id(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bytes sub_session_key = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 50)) { + auto str = _internal_mutable_sub_session_key(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bool ranging_disabled = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 56)) { + _Internal::set_has_ranging_disabled(&has_bits); + ranging_disabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int64 device_unique_id = 8; + case 8: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 64)) { + _Internal::set_has_device_unique_id(&has_bits); + device_unique_id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bool is_distance_supported = 9 [default = true]; + case 9: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 72)) { + _Internal::set_has_is_distance_supported(&has_bits); + is_distance_supported_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bool is_azimuth_supported = 10 [default = true]; + case 10: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 80)) { + _Internal::set_has_is_azimuth_supported(&has_bits); + is_azimuth_supported_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bool is_elevation_supported = 11 [default = false]; + case 11: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 88)) { + _Internal::set_has_is_elevation_supported(&has_bits); + is_elevation_supported_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional float min_slot_duration_ms = 12 [default = 2]; + case 12: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 101)) { + _Internal::set_has_min_slot_duration_ms(&has_bits); + min_slot_duration_ms_ = ::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr); + ptr += sizeof(float); + } else + goto handle_unusual; + continue; + // repeated int32 supported_ntf_configs = 13 [packed = true]; + case 13: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 106)) { + ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_supported_ntf_configs(), ptr, ctx); + CHK_(ptr); + } else if (static_cast(tag) == 104) { + _internal_add_supported_ntf_configs(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr)); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bool is_ranging_interval_reconfigure_supported = 14 [default = false]; + case 14: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 112)) { + _Internal::set_has_is_ranging_interval_reconfigure_supported(&has_bits); + is_ranging_interval_reconfigure_supported_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // repeated int32 supported_slot_durations = 15 [packed = true]; + case 15: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 122)) { + ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_supported_slot_durations(), ptr, ctx); + CHK_(ptr); + } else if (static_cast(tag) == 120) { + _internal_add_supported_slot_durations(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr)); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // repeated int32 supported_ranging_update_rates = 16 [packed = true]; + case 16: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 130)) { + ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedInt32Parser(_internal_mutable_supported_ranging_update_rates(), ptr, ctx); + CHK_(ptr); + } else if (static_cast(tag) == 128) { + _internal_add_supported_ranging_update_rates(::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr)); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 chip_count = 17 [default = 1]; + case 17: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 136)) { + _Internal::set_has_chip_count(&has_bits); + chip_count_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // repeated .nearby.presence.UwbMultiChipInfo multi_chip_info = 18; + case 18: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 146)) { + ptr -= 2; + do { + ptr += 2; + ptr = ctx->ParseMessage(_internal_add_multi_chip_info(), ptr); + CHK_(ptr); + if (!ctx->DataAvailable(ptr)) break; + } while (::PROTOBUF_NAMESPACE_ID::internal::ExpectTag<146>(ptr)); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* UwbControleeCapabilities::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:nearby.presence.UwbControleeCapabilities) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + // optional bytes controlee_address = 1; + if (cached_has_bits & 0x00000001u) { + target = stream->WriteBytesMaybeAliased( + 1, this->_internal_controlee_address(), target); + } + + // repeated int32 supported_config_ids = 2 [packed = true]; + { + int byte_size = _supported_config_ids_cached_byte_size_.load(std::memory_order_relaxed); + if (byte_size > 0) { + target = stream->WriteInt32Packed( + 2, _internal_supported_config_ids(), byte_size, target); + } + } + + // repeated int32 supported_channels = 3 [packed = true]; + { + int byte_size = _supported_channels_cached_byte_size_.load(std::memory_order_relaxed); + if (byte_size > 0) { + target = stream->WriteInt32Packed( + 3, _internal_supported_channels(), byte_size, target); + } + } + + // optional int32 min_ranging_interval_ms = 4; + if (cached_has_bits & 0x00000010u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(4, this->_internal_min_ranging_interval_ms(), target); + } + + // optional bytes sub_session_id = 5; + if (cached_has_bits & 0x00000002u) { + target = stream->WriteBytesMaybeAliased( + 5, this->_internal_sub_session_id(), target); + } + + // optional bytes sub_session_key = 6; + if (cached_has_bits & 0x00000004u) { + target = stream->WriteBytesMaybeAliased( + 6, this->_internal_sub_session_key(), target); + } + + // optional bool ranging_disabled = 7; + if (cached_has_bits & 0x00000020u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(7, this->_internal_ranging_disabled(), target); + } + + // optional int64 device_unique_id = 8; + if (cached_has_bits & 0x00000008u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt64ToArray(8, this->_internal_device_unique_id(), target); + } + + // optional bool is_distance_supported = 9 [default = true]; + if (cached_has_bits & 0x00000200u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(9, this->_internal_is_distance_supported(), target); + } + + // optional bool is_azimuth_supported = 10 [default = true]; + if (cached_has_bits & 0x00000400u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(10, this->_internal_is_azimuth_supported(), target); + } + + // optional bool is_elevation_supported = 11 [default = false]; + if (cached_has_bits & 0x00000040u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(11, this->_internal_is_elevation_supported(), target); + } + + // optional float min_slot_duration_ms = 12 [default = 2]; + if (cached_has_bits & 0x00000800u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteFloatToArray(12, this->_internal_min_slot_duration_ms(), target); + } + + // repeated int32 supported_ntf_configs = 13 [packed = true]; + { + int byte_size = _supported_ntf_configs_cached_byte_size_.load(std::memory_order_relaxed); + if (byte_size > 0) { + target = stream->WriteInt32Packed( + 13, _internal_supported_ntf_configs(), byte_size, target); + } + } + + // optional bool is_ranging_interval_reconfigure_supported = 14 [default = false]; + if (cached_has_bits & 0x00000080u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(14, this->_internal_is_ranging_interval_reconfigure_supported(), target); + } + + // repeated int32 supported_slot_durations = 15 [packed = true]; + { + int byte_size = _supported_slot_durations_cached_byte_size_.load(std::memory_order_relaxed); + if (byte_size > 0) { + target = stream->WriteInt32Packed( + 15, _internal_supported_slot_durations(), byte_size, target); + } + } + + // repeated int32 supported_ranging_update_rates = 16 [packed = true]; + { + int byte_size = _supported_ranging_update_rates_cached_byte_size_.load(std::memory_order_relaxed); + if (byte_size > 0) { + target = stream->WriteInt32Packed( + 16, _internal_supported_ranging_update_rates(), byte_size, target); + } + } + + // optional int32 chip_count = 17 [default = 1]; + if (cached_has_bits & 0x00000100u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(17, this->_internal_chip_count(), target); + } + + // repeated .nearby.presence.UwbMultiChipInfo multi_chip_info = 18; + for (unsigned int i = 0, + n = static_cast(this->_internal_multi_chip_info_size()); i < n; i++) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + InternalWriteMessage(18, this->_internal_multi_chip_info(i), target, stream); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:nearby.presence.UwbControleeCapabilities) + return target; +} + +size_t UwbControleeCapabilities::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:nearby.presence.UwbControleeCapabilities) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated int32 supported_config_ids = 2 [packed = true]; + { + size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + Int32Size(this->supported_config_ids_); + if (data_size > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( + static_cast(data_size)); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); + _supported_config_ids_cached_byte_size_.store(cached_size, + std::memory_order_relaxed); + total_size += data_size; + } + + // repeated int32 supported_channels = 3 [packed = true]; + { + size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + Int32Size(this->supported_channels_); + if (data_size > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( + static_cast(data_size)); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); + _supported_channels_cached_byte_size_.store(cached_size, + std::memory_order_relaxed); + total_size += data_size; + } + + // repeated int32 supported_ntf_configs = 13 [packed = true]; + { + size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + Int32Size(this->supported_ntf_configs_); + if (data_size > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( + static_cast(data_size)); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); + _supported_ntf_configs_cached_byte_size_.store(cached_size, + std::memory_order_relaxed); + total_size += data_size; + } + + // repeated int32 supported_slot_durations = 15 [packed = true]; + { + size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + Int32Size(this->supported_slot_durations_); + if (data_size > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( + static_cast(data_size)); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); + _supported_slot_durations_cached_byte_size_.store(cached_size, + std::memory_order_relaxed); + total_size += data_size; + } + + // repeated int32 supported_ranging_update_rates = 16 [packed = true]; + { + size_t data_size = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite:: + Int32Size(this->supported_ranging_update_rates_); + if (data_size > 0) { + total_size += 2 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( + static_cast(data_size)); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); + _supported_ranging_update_rates_cached_byte_size_.store(cached_size, + std::memory_order_relaxed); + total_size += data_size; + } + + // repeated .nearby.presence.UwbMultiChipInfo multi_chip_info = 18; + total_size += 2UL * this->_internal_multi_chip_info_size(); + for (const auto& msg : this->multi_chip_info_) { + total_size += + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::MessageSize(msg); + } + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x000000ffu) { + // optional bytes controlee_address = 1; + if (cached_has_bits & 0x00000001u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_controlee_address()); + } + + // optional bytes sub_session_id = 5; + if (cached_has_bits & 0x00000002u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_sub_session_id()); + } + + // optional bytes sub_session_key = 6; + if (cached_has_bits & 0x00000004u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_sub_session_key()); + } + + // optional int64 device_unique_id = 8; + if (cached_has_bits & 0x00000008u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int64SizePlusOne(this->_internal_device_unique_id()); + } + + // optional int32 min_ranging_interval_ms = 4; + if (cached_has_bits & 0x00000010u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_min_ranging_interval_ms()); + } + + // optional bool ranging_disabled = 7; + if (cached_has_bits & 0x00000020u) { + total_size += 1 + 1; + } + + // optional bool is_elevation_supported = 11 [default = false]; + if (cached_has_bits & 0x00000040u) { + total_size += 1 + 1; + } + + // optional bool is_ranging_interval_reconfigure_supported = 14 [default = false]; + if (cached_has_bits & 0x00000080u) { + total_size += 1 + 1; + } + + } + if (cached_has_bits & 0x00000f00u) { + // optional int32 chip_count = 17 [default = 1]; + if (cached_has_bits & 0x00000100u) { + total_size += 2 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( + this->_internal_chip_count()); + } + + // optional bool is_distance_supported = 9 [default = true]; + if (cached_has_bits & 0x00000200u) { + total_size += 1 + 1; + } + + // optional bool is_azimuth_supported = 10 [default = true]; + if (cached_has_bits & 0x00000400u) { + total_size += 1 + 1; + } + + // optional float min_slot_duration_ms = 12 [default = 2]; + if (cached_has_bits & 0x00000800u) { + total_size += 1 + 4; + } + + } + return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UwbControleeCapabilities::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, + UwbControleeCapabilities::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UwbControleeCapabilities::GetClassData() const { return &_class_data_; } + +void UwbControleeCapabilities::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, + const ::PROTOBUF_NAMESPACE_ID::Message& from) { + static_cast(to)->MergeFrom( + static_cast(from)); +} + + +void UwbControleeCapabilities::MergeFrom(const UwbControleeCapabilities& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:nearby.presence.UwbControleeCapabilities) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + supported_config_ids_.MergeFrom(from.supported_config_ids_); + supported_channels_.MergeFrom(from.supported_channels_); + supported_ntf_configs_.MergeFrom(from.supported_ntf_configs_); + supported_slot_durations_.MergeFrom(from.supported_slot_durations_); + supported_ranging_update_rates_.MergeFrom(from.supported_ranging_update_rates_); + multi_chip_info_.MergeFrom(from.multi_chip_info_); + cached_has_bits = from._has_bits_[0]; + if (cached_has_bits & 0x000000ffu) { + if (cached_has_bits & 0x00000001u) { + _internal_set_controlee_address(from._internal_controlee_address()); + } + if (cached_has_bits & 0x00000002u) { + _internal_set_sub_session_id(from._internal_sub_session_id()); + } + if (cached_has_bits & 0x00000004u) { + _internal_set_sub_session_key(from._internal_sub_session_key()); + } + if (cached_has_bits & 0x00000008u) { + device_unique_id_ = from.device_unique_id_; + } + if (cached_has_bits & 0x00000010u) { + min_ranging_interval_ms_ = from.min_ranging_interval_ms_; + } + if (cached_has_bits & 0x00000020u) { + ranging_disabled_ = from.ranging_disabled_; + } + if (cached_has_bits & 0x00000040u) { + is_elevation_supported_ = from.is_elevation_supported_; + } + if (cached_has_bits & 0x00000080u) { + is_ranging_interval_reconfigure_supported_ = from.is_ranging_interval_reconfigure_supported_; + } + _has_bits_[0] |= cached_has_bits; + } + if (cached_has_bits & 0x00000f00u) { + if (cached_has_bits & 0x00000100u) { + chip_count_ = from.chip_count_; + } + if (cached_has_bits & 0x00000200u) { + is_distance_supported_ = from.is_distance_supported_; + } + if (cached_has_bits & 0x00000400u) { + is_azimuth_supported_ = from.is_azimuth_supported_; + } + if (cached_has_bits & 0x00000800u) { + min_slot_duration_ms_ = from.min_slot_duration_ms_; + } + _has_bits_[0] |= cached_has_bits; + } + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void UwbControleeCapabilities::CopyFrom(const UwbControleeCapabilities& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:nearby.presence.UwbControleeCapabilities) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool UwbControleeCapabilities::IsInitialized() const { + return true; +} + +void UwbControleeCapabilities::InternalSwap(UwbControleeCapabilities* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + supported_config_ids_.InternalSwap(&other->supported_config_ids_); + supported_channels_.InternalSwap(&other->supported_channels_); + supported_ntf_configs_.InternalSwap(&other->supported_ntf_configs_); + supported_slot_durations_.InternalSwap(&other->supported_slot_durations_); + supported_ranging_update_rates_.InternalSwap(&other->supported_ranging_update_rates_); + multi_chip_info_.InternalSwap(&other->multi_chip_info_); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &controlee_address_, lhs_arena, + &other->controlee_address_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &sub_session_id_, lhs_arena, + &other->sub_session_id_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &sub_session_key_, lhs_arena, + &other->sub_session_key_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(UwbControleeCapabilities, is_ranging_interval_reconfigure_supported_) + + sizeof(UwbControleeCapabilities::is_ranging_interval_reconfigure_supported_) + - PROTOBUF_FIELD_OFFSET(UwbControleeCapabilities, device_unique_id_)>( + reinterpret_cast(&device_unique_id_), + reinterpret_cast(&other->device_unique_id_)); + swap(chip_count_, other->chip_count_); + swap(is_distance_supported_, other->is_distance_supported_); + swap(is_azimuth_supported_, other->is_azimuth_supported_); + swap(min_slot_duration_ms_, other->min_slot_duration_ms_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata UwbControleeCapabilities::GetMetadata() const { + return ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors( + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter, &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[4]); +} + +// =================================================================== + +class UwbMultiChipInfo::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static void set_has_controlee_address(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } + static void set_has_chip_id(HasBits* has_bits) { + (*has_bits)[0] |= 2u; + } +}; + +UwbMultiChipInfo::UwbMultiChipInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:nearby.presence.UwbMultiChipInfo) +} +UwbMultiChipInfo::UwbMultiChipInfo(const UwbMultiChipInfo& from) + : ::PROTOBUF_NAMESPACE_ID::Message(), + _has_bits_(from._has_bits_) { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + controlee_address_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + controlee_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_controlee_address()) { + controlee_address_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_controlee_address(), + GetArenaForAllocation()); + } + chip_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + chip_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_chip_id()) { + chip_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_chip_id(), + GetArenaForAllocation()); + } + // @@protoc_insertion_point(copy_constructor:nearby.presence.UwbMultiChipInfo) +} + +inline void UwbMultiChipInfo::SharedCtor() { +controlee_address_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + controlee_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +chip_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + chip_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +} + +UwbMultiChipInfo::~UwbMultiChipInfo() { + // @@protoc_insertion_point(destructor:nearby.presence.UwbMultiChipInfo) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +inline void UwbMultiChipInfo::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + controlee_address_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + chip_id_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void UwbMultiChipInfo::ArenaDtor(void* object) { + UwbMultiChipInfo* _this = reinterpret_cast< UwbMultiChipInfo* >(object); + (void)_this; +} +void UwbMultiChipInfo::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void UwbMultiChipInfo::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void UwbMultiChipInfo::Clear() { +// @@protoc_insertion_point(message_clear_start:nearby.presence.UwbMultiChipInfo) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000003u) { + if (cached_has_bits & 0x00000001u) { + controlee_address_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000002u) { + chip_id_.ClearNonDefaultToEmpty(); + } + } + _has_bits_.Clear(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* UwbMultiChipInfo::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // optional bytes controlee_address = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_controlee_address(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional string chip_id = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_chip_id(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + #ifndef NDEBUG + ::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "nearby.presence.UwbMultiChipInfo.chip_id"); + #endif // !NDEBUG + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* UwbMultiChipInfo::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:nearby.presence.UwbMultiChipInfo) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + // optional bytes controlee_address = 1; + if (cached_has_bits & 0x00000001u) { + target = stream->WriteBytesMaybeAliased( + 1, this->_internal_controlee_address(), target); + } + + // optional string chip_id = 2; + if (cached_has_bits & 0x00000002u) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::VerifyUTF8StringNamedField( + this->_internal_chip_id().data(), static_cast(this->_internal_chip_id().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::SERIALIZE, + "nearby.presence.UwbMultiChipInfo.chip_id"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_chip_id(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:nearby.presence.UwbMultiChipInfo) + return target; +} + +size_t UwbMultiChipInfo::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:nearby.presence.UwbMultiChipInfo) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000003u) { + // optional bytes controlee_address = 1; + if (cached_has_bits & 0x00000001u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_controlee_address()); + } + + // optional string chip_id = 2; + if (cached_has_bits & 0x00000002u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_chip_id()); + } + + } + return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UwbMultiChipInfo::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, + UwbMultiChipInfo::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UwbMultiChipInfo::GetClassData() const { return &_class_data_; } + +void UwbMultiChipInfo::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, + const ::PROTOBUF_NAMESPACE_ID::Message& from) { + static_cast(to)->MergeFrom( + static_cast(from)); +} + + +void UwbMultiChipInfo::MergeFrom(const UwbMultiChipInfo& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:nearby.presence.UwbMultiChipInfo) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = from._has_bits_[0]; + if (cached_has_bits & 0x00000003u) { + if (cached_has_bits & 0x00000001u) { + _internal_set_controlee_address(from._internal_controlee_address()); + } + if (cached_has_bits & 0x00000002u) { + _internal_set_chip_id(from._internal_chip_id()); + } + } + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void UwbMultiChipInfo::CopyFrom(const UwbMultiChipInfo& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:nearby.presence.UwbMultiChipInfo) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool UwbMultiChipInfo::IsInitialized() const { + return true; +} + +void UwbMultiChipInfo::InternalSwap(UwbMultiChipInfo* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &controlee_address_, lhs_arena, + &other->controlee_address_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &chip_id_, lhs_arena, + &other->chip_id_, rhs_arena + ); +} + +::PROTOBUF_NAMESPACE_ID::Metadata UwbMultiChipInfo::GetMetadata() const { + return ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors( + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter, &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[5]); +} + +// =================================================================== + +class UwbConnectionInfo::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static void set_has_controller_address(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } + static void set_has_channel(HasBits* has_bits) { + (*has_bits)[0] |= 16u; + } + static void set_has_preamble_index(HasBits* has_bits) { + (*has_bits)[0] |= 32u; + } + static void set_has_config_id(HasBits* has_bits) { + (*has_bits)[0] |= 64u; + } + static void set_has_ranging_interval_ms(HasBits* has_bits) { + (*has_bits)[0] |= 128u; + } + static void set_has_session_id(HasBits* has_bits) { + (*has_bits)[0] |= 256u; + } + static void set_has_vendor_id(HasBits* has_bits) { + (*has_bits)[0] |= 2u; + } + static void set_has_static_sts_iv(HasBits* has_bits) { + (*has_bits)[0] |= 4u; + } + static void set_has_session_key(HasBits* has_bits) { + (*has_bits)[0] |= 8u; + } + static void set_has_ranging_disabled(HasBits* has_bits) { + (*has_bits)[0] |= 512u; + } +}; + +UwbConnectionInfo::UwbConnectionInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:nearby.presence.UwbConnectionInfo) +} +UwbConnectionInfo::UwbConnectionInfo(const UwbConnectionInfo& from) + : ::PROTOBUF_NAMESPACE_ID::Message(), + _has_bits_(from._has_bits_) { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + controller_address_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + controller_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_controller_address()) { + controller_address_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_controller_address(), + GetArenaForAllocation()); + } + vendor_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + vendor_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_vendor_id()) { + vendor_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_vendor_id(), + GetArenaForAllocation()); + } + static_sts_iv_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + static_sts_iv_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_static_sts_iv()) { + static_sts_iv_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_static_sts_iv(), + GetArenaForAllocation()); + } + session_key_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + session_key_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_session_key()) { + session_key_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_session_key(), + GetArenaForAllocation()); + } + ::memcpy(&channel_, &from.channel_, + static_cast(reinterpret_cast(&ranging_disabled_) - + reinterpret_cast(&channel_)) + sizeof(ranging_disabled_)); + // @@protoc_insertion_point(copy_constructor:nearby.presence.UwbConnectionInfo) +} + +inline void UwbConnectionInfo::SharedCtor() { +controller_address_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + controller_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +vendor_id_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + vendor_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +static_sts_iv_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + static_sts_iv_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +session_key_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + session_key_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +::memset(reinterpret_cast(this) + static_cast( + reinterpret_cast(&channel_) - reinterpret_cast(this)), + 0, static_cast(reinterpret_cast(&ranging_disabled_) - + reinterpret_cast(&channel_)) + sizeof(ranging_disabled_)); +} + +UwbConnectionInfo::~UwbConnectionInfo() { + // @@protoc_insertion_point(destructor:nearby.presence.UwbConnectionInfo) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +inline void UwbConnectionInfo::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + controller_address_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + vendor_id_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + static_sts_iv_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + session_key_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void UwbConnectionInfo::ArenaDtor(void* object) { + UwbConnectionInfo* _this = reinterpret_cast< UwbConnectionInfo* >(object); + (void)_this; +} +void UwbConnectionInfo::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void UwbConnectionInfo::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void UwbConnectionInfo::Clear() { +// @@protoc_insertion_point(message_clear_start:nearby.presence.UwbConnectionInfo) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x0000000fu) { + if (cached_has_bits & 0x00000001u) { + controller_address_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000002u) { + vendor_id_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000004u) { + static_sts_iv_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000008u) { + session_key_.ClearNonDefaultToEmpty(); + } + } + if (cached_has_bits & 0x000000f0u) { + ::memset(&channel_, 0, static_cast( + reinterpret_cast(&ranging_interval_ms_) - + reinterpret_cast(&channel_)) + sizeof(ranging_interval_ms_)); + } + if (cached_has_bits & 0x00000300u) { + ::memset(&session_id_, 0, static_cast( + reinterpret_cast(&ranging_disabled_) - + reinterpret_cast(&session_id_)) + sizeof(ranging_disabled_)); + } + _has_bits_.Clear(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* UwbConnectionInfo::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // optional bytes controller_address = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 10)) { + auto str = _internal_mutable_controller_address(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 channel = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 16)) { + _Internal::set_has_channel(&has_bits); + channel_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 preamble_index = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 24)) { + _Internal::set_has_preamble_index(&has_bits); + preamble_index_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 config_id = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 32)) { + _Internal::set_has_config_id(&has_bits); + config_id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 ranging_interval_ms = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 40)) { + _Internal::set_has_ranging_interval_ms(&has_bits); + ranging_interval_ms_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional int32 session_id = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 48)) { + _Internal::set_has_session_id(&has_bits); + session_id_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bytes vendor_id = 7; + case 7: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 58)) { + auto str = _internal_mutable_vendor_id(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bytes static_sts_iv = 8; + case 8: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 66)) { + auto str = _internal_mutable_static_sts_iv(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bytes session_key = 9; + case 9: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 74)) { + auto str = _internal_mutable_session_key(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bool ranging_disabled = 10; + case 10: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 80)) { + _Internal::set_has_ranging_disabled(&has_bits); + ranging_disabled_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* UwbConnectionInfo::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:nearby.presence.UwbConnectionInfo) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + // optional bytes controller_address = 1; + if (cached_has_bits & 0x00000001u) { + target = stream->WriteBytesMaybeAliased( + 1, this->_internal_controller_address(), target); + } + + // optional int32 channel = 2; + if (cached_has_bits & 0x00000010u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(2, this->_internal_channel(), target); + } + + // optional int32 preamble_index = 3; + if (cached_has_bits & 0x00000020u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(3, this->_internal_preamble_index(), target); + } + + // optional int32 config_id = 4; + if (cached_has_bits & 0x00000040u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(4, this->_internal_config_id(), target); + } + + // optional int32 ranging_interval_ms = 5; + if (cached_has_bits & 0x00000080u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(5, this->_internal_ranging_interval_ms(), target); + } + + // optional int32 session_id = 6; + if (cached_has_bits & 0x00000100u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(6, this->_internal_session_id(), target); + } + + // optional bytes vendor_id = 7; + if (cached_has_bits & 0x00000002u) { + target = stream->WriteBytesMaybeAliased( + 7, this->_internal_vendor_id(), target); + } + + // optional bytes static_sts_iv = 8; + if (cached_has_bits & 0x00000004u) { + target = stream->WriteBytesMaybeAliased( + 8, this->_internal_static_sts_iv(), target); + } + + // optional bytes session_key = 9; + if (cached_has_bits & 0x00000008u) { + target = stream->WriteBytesMaybeAliased( + 9, this->_internal_session_key(), target); + } + + // optional bool ranging_disabled = 10; + if (cached_has_bits & 0x00000200u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(10, this->_internal_ranging_disabled(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:nearby.presence.UwbConnectionInfo) + return target; +} + +size_t UwbConnectionInfo::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:nearby.presence.UwbConnectionInfo) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x000000ffu) { + // optional bytes controller_address = 1; + if (cached_has_bits & 0x00000001u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_controller_address()); + } + + // optional bytes vendor_id = 7; + if (cached_has_bits & 0x00000002u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_vendor_id()); + } + + // optional bytes static_sts_iv = 8; + if (cached_has_bits & 0x00000004u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_static_sts_iv()); + } + + // optional bytes session_key = 9; + if (cached_has_bits & 0x00000008u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_session_key()); + } + + // optional int32 channel = 2; + if (cached_has_bits & 0x00000010u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_channel()); + } + + // optional int32 preamble_index = 3; + if (cached_has_bits & 0x00000020u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_preamble_index()); + } + + // optional int32 config_id = 4; + if (cached_has_bits & 0x00000040u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_config_id()); + } + + // optional int32 ranging_interval_ms = 5; + if (cached_has_bits & 0x00000080u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_ranging_interval_ms()); + } + + } + if (cached_has_bits & 0x00000300u) { + // optional int32 session_id = 6; + if (cached_has_bits & 0x00000100u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_session_id()); + } + + // optional bool ranging_disabled = 10; + if (cached_has_bits & 0x00000200u) { + total_size += 1 + 1; + } + + } + return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData UwbConnectionInfo::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, + UwbConnectionInfo::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*UwbConnectionInfo::GetClassData() const { return &_class_data_; } + +void UwbConnectionInfo::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, + const ::PROTOBUF_NAMESPACE_ID::Message& from) { + static_cast(to)->MergeFrom( + static_cast(from)); +} + + +void UwbConnectionInfo::MergeFrom(const UwbConnectionInfo& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:nearby.presence.UwbConnectionInfo) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = from._has_bits_[0]; + if (cached_has_bits & 0x000000ffu) { + if (cached_has_bits & 0x00000001u) { + _internal_set_controller_address(from._internal_controller_address()); + } + if (cached_has_bits & 0x00000002u) { + _internal_set_vendor_id(from._internal_vendor_id()); + } + if (cached_has_bits & 0x00000004u) { + _internal_set_static_sts_iv(from._internal_static_sts_iv()); + } + if (cached_has_bits & 0x00000008u) { + _internal_set_session_key(from._internal_session_key()); + } + if (cached_has_bits & 0x00000010u) { + channel_ = from.channel_; + } + if (cached_has_bits & 0x00000020u) { + preamble_index_ = from.preamble_index_; + } + if (cached_has_bits & 0x00000040u) { + config_id_ = from.config_id_; + } + if (cached_has_bits & 0x00000080u) { + ranging_interval_ms_ = from.ranging_interval_ms_; + } + _has_bits_[0] |= cached_has_bits; + } + if (cached_has_bits & 0x00000300u) { + if (cached_has_bits & 0x00000100u) { + session_id_ = from.session_id_; + } + if (cached_has_bits & 0x00000200u) { + ranging_disabled_ = from.ranging_disabled_; + } + _has_bits_[0] |= cached_has_bits; + } + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void UwbConnectionInfo::CopyFrom(const UwbConnectionInfo& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:nearby.presence.UwbConnectionInfo) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool UwbConnectionInfo::IsInitialized() const { + return true; +} + +void UwbConnectionInfo::InternalSwap(UwbConnectionInfo* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &controller_address_, lhs_arena, + &other->controller_address_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &vendor_id_, lhs_arena, + &other->vendor_id_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &static_sts_iv_, lhs_arena, + &other->static_sts_iv_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &session_key_, lhs_arena, + &other->session_key_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(UwbConnectionInfo, ranging_disabled_) + + sizeof(UwbConnectionInfo::ranging_disabled_) + - PROTOBUF_FIELD_OFFSET(UwbConnectionInfo, channel_)>( + reinterpret_cast(&channel_), + reinterpret_cast(&other->channel_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata UwbConnectionInfo::GetMetadata() const { + return ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors( + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter, &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[6]); +} + +// =================================================================== + +class ControlFrame::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static void set_has_type(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } +}; + +ControlFrame::ControlFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:nearby.presence.ControlFrame) +} +ControlFrame::ControlFrame(const ControlFrame& from) + : ::PROTOBUF_NAMESPACE_ID::Message(), + _has_bits_(from._has_bits_) { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + type_ = from.type_; + // @@protoc_insertion_point(copy_constructor:nearby.presence.ControlFrame) +} + +inline void ControlFrame::SharedCtor() { +type_ = 0; +} + +ControlFrame::~ControlFrame() { + // @@protoc_insertion_point(destructor:nearby.presence.ControlFrame) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +inline void ControlFrame::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); +} + +void ControlFrame::ArenaDtor(void* object) { + ControlFrame* _this = reinterpret_cast< ControlFrame* >(object); + (void)_this; +} +void ControlFrame::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void ControlFrame::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void ControlFrame::Clear() { +// @@protoc_insertion_point(message_clear_start:nearby.presence.ControlFrame) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + type_ = 0; + _has_bits_.Clear(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* ControlFrame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // optional .nearby.presence.ControlFrame.ControlType type = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + uint64_t val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + if (PROTOBUF_PREDICT_TRUE(::nearby::presence::ControlFrame_ControlType_IsValid(val))) { + _internal_set_type(static_cast<::nearby::presence::ControlFrame_ControlType>(val)); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::WriteVarint(1, val, mutable_unknown_fields()); + } + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* ControlFrame::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:nearby.presence.ControlFrame) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + // optional .nearby.presence.ControlFrame.ControlType type = 1; + if (cached_has_bits & 0x00000001u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 1, this->_internal_type(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:nearby.presence.ControlFrame) + return target; +} + +size_t ControlFrame::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:nearby.presence.ControlFrame) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // optional .nearby.presence.ControlFrame.ControlType type = 1; + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000001u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_type()); + } + + return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData ControlFrame::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, + ControlFrame::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*ControlFrame::GetClassData() const { return &_class_data_; } + +void ControlFrame::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, + const ::PROTOBUF_NAMESPACE_ID::Message& from) { + static_cast(to)->MergeFrom( + static_cast(from)); +} + + +void ControlFrame::MergeFrom(const ControlFrame& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:nearby.presence.ControlFrame) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + if (from._internal_has_type()) { + _internal_set_type(from._internal_type()); + } + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void ControlFrame::CopyFrom(const ControlFrame& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:nearby.presence.ControlFrame) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ControlFrame::IsInitialized() const { + return true; +} + +void ControlFrame::InternalSwap(ControlFrame* other) { + using std::swap; + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + swap(type_, other->type_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata ControlFrame::GetMetadata() const { + return ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors( + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter, &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[7]); +} + +// =================================================================== + +class PresenceAuthenticationFrame::_Internal { + public: + using HasBits = decltype(std::declval()._has_bits_); + static void set_has_version(HasBits* has_bits) { + (*has_bits)[0] |= 8u; + } + static void set_has_private_key_signature(HasBits* has_bits) { + (*has_bits)[0] |= 1u; + } + static void set_has_shared_credential_id_hash(HasBits* has_bits) { + (*has_bits)[0] |= 2u; + } + static void set_has_credential_id_hash(HasBits* has_bits) { + (*has_bits)[0] |= 4u; + } +}; + +PresenceAuthenticationFrame::PresenceAuthenticationFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned) + : ::PROTOBUF_NAMESPACE_ID::Message(arena, is_message_owned) { + SharedCtor(); + if (!is_message_owned) { + RegisterArenaDtor(arena); + } + // @@protoc_insertion_point(arena_constructor:nearby.presence.PresenceAuthenticationFrame) +} +PresenceAuthenticationFrame::PresenceAuthenticationFrame(const PresenceAuthenticationFrame& from) + : ::PROTOBUF_NAMESPACE_ID::Message(), + _has_bits_(from._has_bits_) { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + private_key_signature_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + private_key_signature_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_private_key_signature()) { + private_key_signature_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_private_key_signature(), + GetArenaForAllocation()); + } + shared_credential_id_hash_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + shared_credential_id_hash_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_shared_credential_id_hash()) { + shared_credential_id_hash_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_shared_credential_id_hash(), + GetArenaForAllocation()); + } + credential_id_hash_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + #ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + credential_id_hash_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + #endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (from._internal_has_credential_id_hash()) { + credential_id_hash_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, from._internal_credential_id_hash(), + GetArenaForAllocation()); + } + version_ = from.version_; + // @@protoc_insertion_point(copy_constructor:nearby.presence.PresenceAuthenticationFrame) +} + +inline void PresenceAuthenticationFrame::SharedCtor() { +private_key_signature_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + private_key_signature_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +shared_credential_id_hash_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + shared_credential_id_hash_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +credential_id_hash_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + credential_id_hash_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING +version_ = 0; +} + +PresenceAuthenticationFrame::~PresenceAuthenticationFrame() { + // @@protoc_insertion_point(destructor:nearby.presence.PresenceAuthenticationFrame) + if (GetArenaForAllocation() != nullptr) return; + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +inline void PresenceAuthenticationFrame::SharedDtor() { + GOOGLE_DCHECK(GetArenaForAllocation() == nullptr); + private_key_signature_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + shared_credential_id_hash_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + credential_id_hash_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void PresenceAuthenticationFrame::ArenaDtor(void* object) { + PresenceAuthenticationFrame* _this = reinterpret_cast< PresenceAuthenticationFrame* >(object); + (void)_this; +} +void PresenceAuthenticationFrame::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void PresenceAuthenticationFrame::SetCachedSize(int size) const { + _cached_size_.Set(size); +} + +void PresenceAuthenticationFrame::Clear() { +// @@protoc_insertion_point(message_clear_start:nearby.presence.PresenceAuthenticationFrame) + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x00000007u) { + if (cached_has_bits & 0x00000001u) { + private_key_signature_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000002u) { + shared_credential_id_hash_.ClearNonDefaultToEmpty(); + } + if (cached_has_bits & 0x00000004u) { + credential_id_hash_.ClearNonDefaultToEmpty(); + } + } + version_ = 0; + _has_bits_.Clear(); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* PresenceAuthenticationFrame::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + _Internal::HasBits has_bits{}; + while (!ctx->Done(&ptr)) { + uint32_t tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + switch (tag >> 3) { + // optional int32 version = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 8)) { + _Internal::set_has_version(&has_bits); + version_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint32(&ptr); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bytes private_key_signature = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 18)) { + auto str = _internal_mutable_private_key_signature(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bytes shared_credential_id_hash = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 26)) { + auto str = _internal_mutable_shared_credential_id_hash(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + // optional bytes credential_id_hash = 4 [deprecated = true]; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast(tag) == 34)) { + auto str = _internal_mutable_credential_id_hash(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else + goto handle_unusual; + continue; + default: + goto handle_unusual; + } // switch + handle_unusual: + if ((tag == 0) || ((tag & 7) == 4)) { + CHK_(ptr); + ctx->SetLastTag(tag); + goto message_done; + } + ptr = UnknownFieldParse( + tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + } // while +message_done: + _has_bits_.Or(has_bits); + return ptr; +failure: + ptr = nullptr; + goto message_done; +#undef CHK_ +} + +uint8_t* PresenceAuthenticationFrame::_InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:nearby.presence.PresenceAuthenticationFrame) + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + // optional int32 version = 1; + if (cached_has_bits & 0x00000008u) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteInt32ToArray(1, this->_internal_version(), target); + } + + // optional bytes private_key_signature = 2; + if (cached_has_bits & 0x00000001u) { + target = stream->WriteBytesMaybeAliased( + 2, this->_internal_private_key_signature(), target); + } + + // optional bytes shared_credential_id_hash = 3; + if (cached_has_bits & 0x00000002u) { + target = stream->WriteBytesMaybeAliased( + 3, this->_internal_shared_credential_id_hash(), target); + } + + // optional bytes credential_id_hash = 4 [deprecated = true]; + if (cached_has_bits & 0x00000004u) { + target = stream->WriteBytesMaybeAliased( + 4, this->_internal_credential_id_hash(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:nearby.presence.PresenceAuthenticationFrame) + return target; +} + +size_t PresenceAuthenticationFrame::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:nearby.presence.PresenceAuthenticationFrame) + size_t total_size = 0; + + uint32_t cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + cached_has_bits = _has_bits_[0]; + if (cached_has_bits & 0x0000000fu) { + // optional bytes private_key_signature = 2; + if (cached_has_bits & 0x00000001u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_private_key_signature()); + } + + // optional bytes shared_credential_id_hash = 3; + if (cached_has_bits & 0x00000002u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_shared_credential_id_hash()); + } + + // optional bytes credential_id_hash = 4 [deprecated = true]; + if (cached_has_bits & 0x00000004u) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_credential_id_hash()); + } + + // optional int32 version = 1; + if (cached_has_bits & 0x00000008u) { + total_size += ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32SizePlusOne(this->_internal_version()); + } + + } + return MaybeComputeUnknownFieldsSize(total_size, &_cached_size_); +} + +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData PresenceAuthenticationFrame::_class_data_ = { + ::PROTOBUF_NAMESPACE_ID::Message::CopyWithSizeCheck, + PresenceAuthenticationFrame::MergeImpl +}; +const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*PresenceAuthenticationFrame::GetClassData() const { return &_class_data_; } + +void PresenceAuthenticationFrame::MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, + const ::PROTOBUF_NAMESPACE_ID::Message& from) { + static_cast(to)->MergeFrom( + static_cast(from)); +} + + +void PresenceAuthenticationFrame::MergeFrom(const PresenceAuthenticationFrame& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:nearby.presence.PresenceAuthenticationFrame) + GOOGLE_DCHECK_NE(&from, this); + uint32_t cached_has_bits = 0; + (void) cached_has_bits; + + cached_has_bits = from._has_bits_[0]; + if (cached_has_bits & 0x0000000fu) { + if (cached_has_bits & 0x00000001u) { + _internal_set_private_key_signature(from._internal_private_key_signature()); + } + if (cached_has_bits & 0x00000002u) { + _internal_set_shared_credential_id_hash(from._internal_shared_credential_id_hash()); + } + if (cached_has_bits & 0x00000004u) { + _internal_set_credential_id_hash(from._internal_credential_id_hash()); + } + if (cached_has_bits & 0x00000008u) { + version_ = from.version_; + } + _has_bits_[0] |= cached_has_bits; + } + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); +} + +void PresenceAuthenticationFrame::CopyFrom(const PresenceAuthenticationFrame& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:nearby.presence.PresenceAuthenticationFrame) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool PresenceAuthenticationFrame::IsInitialized() const { + return true; +} + +void PresenceAuthenticationFrame::InternalSwap(PresenceAuthenticationFrame* other) { + using std::swap; + auto* lhs_arena = GetArenaForAllocation(); + auto* rhs_arena = other->GetArenaForAllocation(); + _internal_metadata_.InternalSwap(&other->_internal_metadata_); + swap(_has_bits_[0], other->_has_bits_[0]); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &private_key_signature_, lhs_arena, + &other->private_key_signature_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &shared_credential_id_hash_, lhs_arena, + &other->shared_credential_id_hash_, rhs_arena + ); + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::InternalSwap( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + &credential_id_hash_, lhs_arena, + &other->credential_id_hash_, rhs_arena + ); + swap(version_, other->version_); +} + +::PROTOBUF_NAMESPACE_ID::Metadata PresenceAuthenticationFrame::GetMetadata() const { + return ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors( + &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_getter, &descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto_once, + file_level_metadata_presence_2fproto_2fpresence_5fframe_2eproto[8]); +} + +// @@protoc_insertion_point(namespace_scope) +} // namespace presence +} // namespace nearby +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::nearby::presence::PresenceFrame* Arena::CreateMaybeMessage< ::nearby::presence::PresenceFrame >(Arena* arena) { + return Arena::CreateMessageInternal< ::nearby::presence::PresenceFrame >(arena); +} +template<> PROTOBUF_NOINLINE ::nearby::presence::V1Frame* Arena::CreateMaybeMessage< ::nearby::presence::V1Frame >(Arena* arena) { + return Arena::CreateMessageInternal< ::nearby::presence::V1Frame >(arena); +} +template<> PROTOBUF_NOINLINE ::nearby::presence::DeviceIdentityFrame* Arena::CreateMaybeMessage< ::nearby::presence::DeviceIdentityFrame >(Arena* arena) { + return Arena::CreateMessageInternal< ::nearby::presence::DeviceIdentityFrame >(arena); +} +template<> PROTOBUF_NOINLINE ::nearby::presence::ConnectionInitFrame* Arena::CreateMaybeMessage< ::nearby::presence::ConnectionInitFrame >(Arena* arena) { + return Arena::CreateMessageInternal< ::nearby::presence::ConnectionInitFrame >(arena); +} +template<> PROTOBUF_NOINLINE ::nearby::presence::UwbControleeCapabilities* Arena::CreateMaybeMessage< ::nearby::presence::UwbControleeCapabilities >(Arena* arena) { + return Arena::CreateMessageInternal< ::nearby::presence::UwbControleeCapabilities >(arena); +} +template<> PROTOBUF_NOINLINE ::nearby::presence::UwbMultiChipInfo* Arena::CreateMaybeMessage< ::nearby::presence::UwbMultiChipInfo >(Arena* arena) { + return Arena::CreateMessageInternal< ::nearby::presence::UwbMultiChipInfo >(arena); +} +template<> PROTOBUF_NOINLINE ::nearby::presence::UwbConnectionInfo* Arena::CreateMaybeMessage< ::nearby::presence::UwbConnectionInfo >(Arena* arena) { + return Arena::CreateMessageInternal< ::nearby::presence::UwbConnectionInfo >(arena); +} +template<> PROTOBUF_NOINLINE ::nearby::presence::ControlFrame* Arena::CreateMaybeMessage< ::nearby::presence::ControlFrame >(Arena* arena) { + return Arena::CreateMessageInternal< ::nearby::presence::ControlFrame >(arena); +} +template<> PROTOBUF_NOINLINE ::nearby::presence::PresenceAuthenticationFrame* Arena::CreateMaybeMessage< ::nearby::presence::PresenceAuthenticationFrame >(Arena* arena) { + return Arena::CreateMessageInternal< ::nearby::presence::PresenceAuthenticationFrame >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/compiled_proto/presence/proto/presence_frame.pb.h b/compiled_proto/presence/proto/presence_frame.pb.h new file mode 100644 index 00000000..ce177b23 --- /dev/null +++ b/compiled_proto/presence/proto/presence_frame.pb.h @@ -0,0 +1,5274 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: presence/proto/presence_frame.proto + +#ifndef GOOGLE_PROTOBUF_INCLUDED_presence_2fproto_2fpresence_5fframe_2eproto +#define GOOGLE_PROTOBUF_INCLUDED_presence_2fproto_2fpresence_5fframe_2eproto + +#include +#include + +#include +#if PROTOBUF_VERSION < 3019000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3019001 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_presence_2fproto_2fpresence_5fframe_2eproto +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_presence_2fproto_2fpresence_5fframe_2eproto { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxiliaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[9] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const uint32_t offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_presence_2fproto_2fpresence_5fframe_2eproto; +namespace nearby { +namespace presence { +class ConnectionInitFrame; +struct ConnectionInitFrameDefaultTypeInternal; +extern ConnectionInitFrameDefaultTypeInternal _ConnectionInitFrame_default_instance_; +class ControlFrame; +struct ControlFrameDefaultTypeInternal; +extern ControlFrameDefaultTypeInternal _ControlFrame_default_instance_; +class DeviceIdentityFrame; +struct DeviceIdentityFrameDefaultTypeInternal; +extern DeviceIdentityFrameDefaultTypeInternal _DeviceIdentityFrame_default_instance_; +class PresenceAuthenticationFrame; +struct PresenceAuthenticationFrameDefaultTypeInternal; +extern PresenceAuthenticationFrameDefaultTypeInternal _PresenceAuthenticationFrame_default_instance_; +class PresenceFrame; +struct PresenceFrameDefaultTypeInternal; +extern PresenceFrameDefaultTypeInternal _PresenceFrame_default_instance_; +class UwbConnectionInfo; +struct UwbConnectionInfoDefaultTypeInternal; +extern UwbConnectionInfoDefaultTypeInternal _UwbConnectionInfo_default_instance_; +class UwbControleeCapabilities; +struct UwbControleeCapabilitiesDefaultTypeInternal; +extern UwbControleeCapabilitiesDefaultTypeInternal _UwbControleeCapabilities_default_instance_; +class UwbMultiChipInfo; +struct UwbMultiChipInfoDefaultTypeInternal; +extern UwbMultiChipInfoDefaultTypeInternal _UwbMultiChipInfo_default_instance_; +class V1Frame; +struct V1FrameDefaultTypeInternal; +extern V1FrameDefaultTypeInternal _V1Frame_default_instance_; +} // namespace presence +} // namespace nearby +PROTOBUF_NAMESPACE_OPEN +template<> ::nearby::presence::ConnectionInitFrame* Arena::CreateMaybeMessage<::nearby::presence::ConnectionInitFrame>(Arena*); +template<> ::nearby::presence::ControlFrame* Arena::CreateMaybeMessage<::nearby::presence::ControlFrame>(Arena*); +template<> ::nearby::presence::DeviceIdentityFrame* Arena::CreateMaybeMessage<::nearby::presence::DeviceIdentityFrame>(Arena*); +template<> ::nearby::presence::PresenceAuthenticationFrame* Arena::CreateMaybeMessage<::nearby::presence::PresenceAuthenticationFrame>(Arena*); +template<> ::nearby::presence::PresenceFrame* Arena::CreateMaybeMessage<::nearby::presence::PresenceFrame>(Arena*); +template<> ::nearby::presence::UwbConnectionInfo* Arena::CreateMaybeMessage<::nearby::presence::UwbConnectionInfo>(Arena*); +template<> ::nearby::presence::UwbControleeCapabilities* Arena::CreateMaybeMessage<::nearby::presence::UwbControleeCapabilities>(Arena*); +template<> ::nearby::presence::UwbMultiChipInfo* Arena::CreateMaybeMessage<::nearby::presence::UwbMultiChipInfo>(Arena*); +template<> ::nearby::presence::V1Frame* Arena::CreateMaybeMessage<::nearby::presence::V1Frame>(Arena*); +PROTOBUF_NAMESPACE_CLOSE +namespace nearby { +namespace presence { + +enum PresenceFrame_Version : int { + PresenceFrame_Version_UNKNOWN_VERSION = 0, + PresenceFrame_Version_VERSION_1 = 1 +}; +bool PresenceFrame_Version_IsValid(int value); +constexpr PresenceFrame_Version PresenceFrame_Version_Version_MIN = PresenceFrame_Version_UNKNOWN_VERSION; +constexpr PresenceFrame_Version PresenceFrame_Version_Version_MAX = PresenceFrame_Version_VERSION_1; +constexpr int PresenceFrame_Version_Version_ARRAYSIZE = PresenceFrame_Version_Version_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* PresenceFrame_Version_descriptor(); +template +inline const std::string& PresenceFrame_Version_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function PresenceFrame_Version_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + PresenceFrame_Version_descriptor(), enum_t_value); +} +inline bool PresenceFrame_Version_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, PresenceFrame_Version* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + PresenceFrame_Version_descriptor(), name, value); +} +enum ControlFrame_ControlType : int { + ControlFrame_ControlType_UNKNOWN_TYPE = 0, + ControlFrame_ControlType_KEEP_ALIVE = 1, + ControlFrame_ControlType_DISCONNECT = 2 +}; +bool ControlFrame_ControlType_IsValid(int value); +constexpr ControlFrame_ControlType ControlFrame_ControlType_ControlType_MIN = ControlFrame_ControlType_UNKNOWN_TYPE; +constexpr ControlFrame_ControlType ControlFrame_ControlType_ControlType_MAX = ControlFrame_ControlType_DISCONNECT; +constexpr int ControlFrame_ControlType_ControlType_ARRAYSIZE = ControlFrame_ControlType_ControlType_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* ControlFrame_ControlType_descriptor(); +template +inline const std::string& ControlFrame_ControlType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function ControlFrame_ControlType_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + ControlFrame_ControlType_descriptor(), enum_t_value); +} +inline bool ControlFrame_ControlType_Parse( + ::PROTOBUF_NAMESPACE_ID::ConstStringParam name, ControlFrame_ControlType* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + ControlFrame_ControlType_descriptor(), name, value); +} +// =================================================================== + +class PresenceFrame final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:nearby.presence.PresenceFrame) */ { + public: + inline PresenceFrame() : PresenceFrame(nullptr) {} + ~PresenceFrame() override; + explicit constexpr PresenceFrame(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PresenceFrame(const PresenceFrame& from); + PresenceFrame(PresenceFrame&& from) noexcept + : PresenceFrame() { + *this = ::std::move(from); + } + + inline PresenceFrame& operator=(const PresenceFrame& from) { + CopyFrom(from); + return *this; + } + inline PresenceFrame& operator=(PresenceFrame&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance); + } + inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PresenceFrame& default_instance() { + return *internal_default_instance(); + } + static inline const PresenceFrame* internal_default_instance() { + return reinterpret_cast( + &_PresenceFrame_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(PresenceFrame& a, PresenceFrame& b) { + a.Swap(&b); + } + inline void Swap(PresenceFrame* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PresenceFrame* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PresenceFrame* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PresenceFrame& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom(const PresenceFrame& from); + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PresenceFrame* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "nearby.presence.PresenceFrame"; + } + protected: + explicit PresenceFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + typedef PresenceFrame_Version Version; + static constexpr Version UNKNOWN_VERSION = + PresenceFrame_Version_UNKNOWN_VERSION; + static constexpr Version VERSION_1 = + PresenceFrame_Version_VERSION_1; + static inline bool Version_IsValid(int value) { + return PresenceFrame_Version_IsValid(value); + } + static constexpr Version Version_MIN = + PresenceFrame_Version_Version_MIN; + static constexpr Version Version_MAX = + PresenceFrame_Version_Version_MAX; + static constexpr int Version_ARRAYSIZE = + PresenceFrame_Version_Version_ARRAYSIZE; + static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* + Version_descriptor() { + return PresenceFrame_Version_descriptor(); + } + template + static inline const std::string& Version_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function Version_Name."); + return PresenceFrame_Version_Name(enum_t_value); + } + static inline bool Version_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name, + Version* value) { + return PresenceFrame_Version_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + enum : int { + kV1FrameFieldNumber = 1, + }; + // optional .nearby.presence.V1Frame v1_frame = 1; + bool has_v1_frame() const; + private: + bool _internal_has_v1_frame() const; + public: + void clear_v1_frame(); + const ::nearby::presence::V1Frame& v1_frame() const; + PROTOBUF_NODISCARD ::nearby::presence::V1Frame* release_v1_frame(); + ::nearby::presence::V1Frame* mutable_v1_frame(); + void set_allocated_v1_frame(::nearby::presence::V1Frame* v1_frame); + private: + const ::nearby::presence::V1Frame& _internal_v1_frame() const; + ::nearby::presence::V1Frame* _internal_mutable_v1_frame(); + public: + void unsafe_arena_set_allocated_v1_frame( + ::nearby::presence::V1Frame* v1_frame); + ::nearby::presence::V1Frame* unsafe_arena_release_v1_frame(); + + // @@protoc_insertion_point(class_scope:nearby.presence.PresenceFrame) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + ::nearby::presence::V1Frame* v1_frame_; + friend struct ::TableStruct_presence_2fproto_2fpresence_5fframe_2eproto; +}; +// ------------------------------------------------------------------- + +class V1Frame final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:nearby.presence.V1Frame) */ { + public: + inline V1Frame() : V1Frame(nullptr) {} + ~V1Frame() override; + explicit constexpr V1Frame(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + V1Frame(const V1Frame& from); + V1Frame(V1Frame&& from) noexcept + : V1Frame() { + *this = ::std::move(from); + } + + inline V1Frame& operator=(const V1Frame& from) { + CopyFrom(from); + return *this; + } + inline V1Frame& operator=(V1Frame&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance); + } + inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const V1Frame& default_instance() { + return *internal_default_instance(); + } + enum MessageCase { + kControlFrame = 1, + kDeviceIdentityFrame = 2, + kConnectionInitFrame = 3, + kUwbControleeCapabilitiesFrame = 4, + kUwbConnectionInfo = 5, + kAuthenticationFrame = 6, + MESSAGE_NOT_SET = 0, + }; + + static inline const V1Frame* internal_default_instance() { + return reinterpret_cast( + &_V1Frame_default_instance_); + } + static constexpr int kIndexInFileMessages = + 1; + + friend void swap(V1Frame& a, V1Frame& b) { + a.Swap(&b); + } + inline void Swap(V1Frame* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(V1Frame* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + V1Frame* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const V1Frame& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom(const V1Frame& from); + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(V1Frame* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "nearby.presence.V1Frame"; + } + protected: + explicit V1Frame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kControlFrameFieldNumber = 1, + kDeviceIdentityFrameFieldNumber = 2, + kConnectionInitFrameFieldNumber = 3, + kUwbControleeCapabilitiesFrameFieldNumber = 4, + kUwbConnectionInfoFieldNumber = 5, + kAuthenticationFrameFieldNumber = 6, + }; + // .nearby.presence.ControlFrame control_frame = 1; + bool has_control_frame() const; + private: + bool _internal_has_control_frame() const; + public: + void clear_control_frame(); + const ::nearby::presence::ControlFrame& control_frame() const; + PROTOBUF_NODISCARD ::nearby::presence::ControlFrame* release_control_frame(); + ::nearby::presence::ControlFrame* mutable_control_frame(); + void set_allocated_control_frame(::nearby::presence::ControlFrame* control_frame); + private: + const ::nearby::presence::ControlFrame& _internal_control_frame() const; + ::nearby::presence::ControlFrame* _internal_mutable_control_frame(); + public: + void unsafe_arena_set_allocated_control_frame( + ::nearby::presence::ControlFrame* control_frame); + ::nearby::presence::ControlFrame* unsafe_arena_release_control_frame(); + + // .nearby.presence.DeviceIdentityFrame device_identity_frame = 2; + bool has_device_identity_frame() const; + private: + bool _internal_has_device_identity_frame() const; + public: + void clear_device_identity_frame(); + const ::nearby::presence::DeviceIdentityFrame& device_identity_frame() const; + PROTOBUF_NODISCARD ::nearby::presence::DeviceIdentityFrame* release_device_identity_frame(); + ::nearby::presence::DeviceIdentityFrame* mutable_device_identity_frame(); + void set_allocated_device_identity_frame(::nearby::presence::DeviceIdentityFrame* device_identity_frame); + private: + const ::nearby::presence::DeviceIdentityFrame& _internal_device_identity_frame() const; + ::nearby::presence::DeviceIdentityFrame* _internal_mutable_device_identity_frame(); + public: + void unsafe_arena_set_allocated_device_identity_frame( + ::nearby::presence::DeviceIdentityFrame* device_identity_frame); + ::nearby::presence::DeviceIdentityFrame* unsafe_arena_release_device_identity_frame(); + + // .nearby.presence.ConnectionInitFrame connection_init_frame = 3; + bool has_connection_init_frame() const; + private: + bool _internal_has_connection_init_frame() const; + public: + void clear_connection_init_frame(); + const ::nearby::presence::ConnectionInitFrame& connection_init_frame() const; + PROTOBUF_NODISCARD ::nearby::presence::ConnectionInitFrame* release_connection_init_frame(); + ::nearby::presence::ConnectionInitFrame* mutable_connection_init_frame(); + void set_allocated_connection_init_frame(::nearby::presence::ConnectionInitFrame* connection_init_frame); + private: + const ::nearby::presence::ConnectionInitFrame& _internal_connection_init_frame() const; + ::nearby::presence::ConnectionInitFrame* _internal_mutable_connection_init_frame(); + public: + void unsafe_arena_set_allocated_connection_init_frame( + ::nearby::presence::ConnectionInitFrame* connection_init_frame); + ::nearby::presence::ConnectionInitFrame* unsafe_arena_release_connection_init_frame(); + + // .nearby.presence.UwbControleeCapabilities uwb_controlee_capabilities_frame = 4; + bool has_uwb_controlee_capabilities_frame() const; + private: + bool _internal_has_uwb_controlee_capabilities_frame() const; + public: + void clear_uwb_controlee_capabilities_frame(); + const ::nearby::presence::UwbControleeCapabilities& uwb_controlee_capabilities_frame() const; + PROTOBUF_NODISCARD ::nearby::presence::UwbControleeCapabilities* release_uwb_controlee_capabilities_frame(); + ::nearby::presence::UwbControleeCapabilities* mutable_uwb_controlee_capabilities_frame(); + void set_allocated_uwb_controlee_capabilities_frame(::nearby::presence::UwbControleeCapabilities* uwb_controlee_capabilities_frame); + private: + const ::nearby::presence::UwbControleeCapabilities& _internal_uwb_controlee_capabilities_frame() const; + ::nearby::presence::UwbControleeCapabilities* _internal_mutable_uwb_controlee_capabilities_frame(); + public: + void unsafe_arena_set_allocated_uwb_controlee_capabilities_frame( + ::nearby::presence::UwbControleeCapabilities* uwb_controlee_capabilities_frame); + ::nearby::presence::UwbControleeCapabilities* unsafe_arena_release_uwb_controlee_capabilities_frame(); + + // .nearby.presence.UwbConnectionInfo uwb_connection_info = 5; + bool has_uwb_connection_info() const; + private: + bool _internal_has_uwb_connection_info() const; + public: + void clear_uwb_connection_info(); + const ::nearby::presence::UwbConnectionInfo& uwb_connection_info() const; + PROTOBUF_NODISCARD ::nearby::presence::UwbConnectionInfo* release_uwb_connection_info(); + ::nearby::presence::UwbConnectionInfo* mutable_uwb_connection_info(); + void set_allocated_uwb_connection_info(::nearby::presence::UwbConnectionInfo* uwb_connection_info); + private: + const ::nearby::presence::UwbConnectionInfo& _internal_uwb_connection_info() const; + ::nearby::presence::UwbConnectionInfo* _internal_mutable_uwb_connection_info(); + public: + void unsafe_arena_set_allocated_uwb_connection_info( + ::nearby::presence::UwbConnectionInfo* uwb_connection_info); + ::nearby::presence::UwbConnectionInfo* unsafe_arena_release_uwb_connection_info(); + + // .nearby.presence.PresenceAuthenticationFrame authentication_frame = 6; + bool has_authentication_frame() const; + private: + bool _internal_has_authentication_frame() const; + public: + void clear_authentication_frame(); + const ::nearby::presence::PresenceAuthenticationFrame& authentication_frame() const; + PROTOBUF_NODISCARD ::nearby::presence::PresenceAuthenticationFrame* release_authentication_frame(); + ::nearby::presence::PresenceAuthenticationFrame* mutable_authentication_frame(); + void set_allocated_authentication_frame(::nearby::presence::PresenceAuthenticationFrame* authentication_frame); + private: + const ::nearby::presence::PresenceAuthenticationFrame& _internal_authentication_frame() const; + ::nearby::presence::PresenceAuthenticationFrame* _internal_mutable_authentication_frame(); + public: + void unsafe_arena_set_allocated_authentication_frame( + ::nearby::presence::PresenceAuthenticationFrame* authentication_frame); + ::nearby::presence::PresenceAuthenticationFrame* unsafe_arena_release_authentication_frame(); + + void clear_Message(); + MessageCase Message_case() const; + // @@protoc_insertion_point(class_scope:nearby.presence.V1Frame) + private: + class _Internal; + void set_has_control_frame(); + void set_has_device_identity_frame(); + void set_has_connection_init_frame(); + void set_has_uwb_controlee_capabilities_frame(); + void set_has_uwb_connection_info(); + void set_has_authentication_frame(); + + inline bool has_Message() const; + inline void clear_has_Message(); + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + union MessageUnion { + constexpr MessageUnion() : _constinit_{} {} + ::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized _constinit_; + ::nearby::presence::ControlFrame* control_frame_; + ::nearby::presence::DeviceIdentityFrame* device_identity_frame_; + ::nearby::presence::ConnectionInitFrame* connection_init_frame_; + ::nearby::presence::UwbControleeCapabilities* uwb_controlee_capabilities_frame_; + ::nearby::presence::UwbConnectionInfo* uwb_connection_info_; + ::nearby::presence::PresenceAuthenticationFrame* authentication_frame_; + } Message_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + uint32_t _oneof_case_[1]; + + friend struct ::TableStruct_presence_2fproto_2fpresence_5fframe_2eproto; +}; +// ------------------------------------------------------------------- + +class DeviceIdentityFrame final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:nearby.presence.DeviceIdentityFrame) */ { + public: + inline DeviceIdentityFrame() : DeviceIdentityFrame(nullptr) {} + ~DeviceIdentityFrame() override; + explicit constexpr DeviceIdentityFrame(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + DeviceIdentityFrame(const DeviceIdentityFrame& from); + DeviceIdentityFrame(DeviceIdentityFrame&& from) noexcept + : DeviceIdentityFrame() { + *this = ::std::move(from); + } + + inline DeviceIdentityFrame& operator=(const DeviceIdentityFrame& from) { + CopyFrom(from); + return *this; + } + inline DeviceIdentityFrame& operator=(DeviceIdentityFrame&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance); + } + inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const DeviceIdentityFrame& default_instance() { + return *internal_default_instance(); + } + static inline const DeviceIdentityFrame* internal_default_instance() { + return reinterpret_cast( + &_DeviceIdentityFrame_default_instance_); + } + static constexpr int kIndexInFileMessages = + 2; + + friend void swap(DeviceIdentityFrame& a, DeviceIdentityFrame& b) { + a.Swap(&b); + } + inline void Swap(DeviceIdentityFrame* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(DeviceIdentityFrame* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + DeviceIdentityFrame* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const DeviceIdentityFrame& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom(const DeviceIdentityFrame& from); + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(DeviceIdentityFrame* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "nearby.presence.DeviceIdentityFrame"; + } + protected: + explicit DeviceIdentityFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kActionFieldNumber = 5, + kDeviceNameFieldNumber = 1, + kBluetoothMacAddressFieldNumber = 2, + kDeviceImageUrlFieldNumber = 3, + kModelIdFieldNumber = 4, + kDeviceModelNameFieldNumber = 6, + kDeviceTypeFieldNumber = 7, + }; + // repeated int32 action = 5 [packed = true]; + int action_size() const; + private: + int _internal_action_size() const; + public: + void clear_action(); + private: + int32_t _internal_action(int index) const; + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + _internal_action() const; + void _internal_add_action(int32_t value); + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + _internal_mutable_action(); + public: + int32_t action(int index) const; + void set_action(int index, int32_t value); + void add_action(int32_t value); + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + action() const; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + mutable_action(); + + // optional string device_name = 1; + bool has_device_name() const; + private: + bool _internal_has_device_name() const; + public: + void clear_device_name(); + const std::string& device_name() const; + template + void set_device_name(ArgT0&& arg0, ArgT... args); + std::string* mutable_device_name(); + PROTOBUF_NODISCARD std::string* release_device_name(); + void set_allocated_device_name(std::string* device_name); + private: + const std::string& _internal_device_name() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_device_name(const std::string& value); + std::string* _internal_mutable_device_name(); + public: + + // optional bytes bluetooth_mac_address = 2; + bool has_bluetooth_mac_address() const; + private: + bool _internal_has_bluetooth_mac_address() const; + public: + void clear_bluetooth_mac_address(); + const std::string& bluetooth_mac_address() const; + template + void set_bluetooth_mac_address(ArgT0&& arg0, ArgT... args); + std::string* mutable_bluetooth_mac_address(); + PROTOBUF_NODISCARD std::string* release_bluetooth_mac_address(); + void set_allocated_bluetooth_mac_address(std::string* bluetooth_mac_address); + private: + const std::string& _internal_bluetooth_mac_address() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_bluetooth_mac_address(const std::string& value); + std::string* _internal_mutable_bluetooth_mac_address(); + public: + + // optional string device_image_url = 3; + bool has_device_image_url() const; + private: + bool _internal_has_device_image_url() const; + public: + void clear_device_image_url(); + const std::string& device_image_url() const; + template + void set_device_image_url(ArgT0&& arg0, ArgT... args); + std::string* mutable_device_image_url(); + PROTOBUF_NODISCARD std::string* release_device_image_url(); + void set_allocated_device_image_url(std::string* device_image_url); + private: + const std::string& _internal_device_image_url() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_device_image_url(const std::string& value); + std::string* _internal_mutable_device_image_url(); + public: + + // optional string model_id = 4; + bool has_model_id() const; + private: + bool _internal_has_model_id() const; + public: + void clear_model_id(); + const std::string& model_id() const; + template + void set_model_id(ArgT0&& arg0, ArgT... args); + std::string* mutable_model_id(); + PROTOBUF_NODISCARD std::string* release_model_id(); + void set_allocated_model_id(std::string* model_id); + private: + const std::string& _internal_model_id() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_model_id(const std::string& value); + std::string* _internal_mutable_model_id(); + public: + + // optional string device_model_name = 6; + bool has_device_model_name() const; + private: + bool _internal_has_device_model_name() const; + public: + void clear_device_model_name(); + const std::string& device_model_name() const; + template + void set_device_model_name(ArgT0&& arg0, ArgT... args); + std::string* mutable_device_model_name(); + PROTOBUF_NODISCARD std::string* release_device_model_name(); + void set_allocated_device_model_name(std::string* device_model_name); + private: + const std::string& _internal_device_model_name() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_device_model_name(const std::string& value); + std::string* _internal_mutable_device_model_name(); + public: + + // optional int32 device_type = 7; + bool has_device_type() const; + private: + bool _internal_has_device_type() const; + public: + void clear_device_type(); + int32_t device_type() const; + void set_device_type(int32_t value); + private: + int32_t _internal_device_type() const; + void _internal_set_device_type(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:nearby.presence.DeviceIdentityFrame) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > action_; + mutable std::atomic _action_cached_byte_size_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr device_name_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr bluetooth_mac_address_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr device_image_url_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr model_id_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr device_model_name_; + int32_t device_type_; + friend struct ::TableStruct_presence_2fproto_2fpresence_5fframe_2eproto; +}; +// ------------------------------------------------------------------- + +class ConnectionInitFrame final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:nearby.presence.ConnectionInitFrame) */ { + public: + inline ConnectionInitFrame() : ConnectionInitFrame(nullptr) {} + ~ConnectionInitFrame() override; + explicit constexpr ConnectionInitFrame(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + ConnectionInitFrame(const ConnectionInitFrame& from); + ConnectionInitFrame(ConnectionInitFrame&& from) noexcept + : ConnectionInitFrame() { + *this = ::std::move(from); + } + + inline ConnectionInitFrame& operator=(const ConnectionInitFrame& from) { + CopyFrom(from); + return *this; + } + inline ConnectionInitFrame& operator=(ConnectionInitFrame&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance); + } + inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const ConnectionInitFrame& default_instance() { + return *internal_default_instance(); + } + static inline const ConnectionInitFrame* internal_default_instance() { + return reinterpret_cast( + &_ConnectionInitFrame_default_instance_); + } + static constexpr int kIndexInFileMessages = + 3; + + friend void swap(ConnectionInitFrame& a, ConnectionInitFrame& b) { + a.Swap(&b); + } + inline void Swap(ConnectionInitFrame* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(ConnectionInitFrame* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + ConnectionInitFrame* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const ConnectionInitFrame& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom(const ConnectionInitFrame& from); + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(ConnectionInitFrame* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "nearby.presence.ConnectionInitFrame"; + } + protected: + explicit ConnectionInitFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kActionsFieldNumber = 1, + kIdentityTypeFieldNumber = 2, + kUwbEnableFieldNumber = 3, + kDeviceUniqueIdFieldNumber = 4, + }; + // repeated int32 actions = 1 [packed = true]; + int actions_size() const; + private: + int _internal_actions_size() const; + public: + void clear_actions(); + private: + int32_t _internal_actions(int index) const; + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + _internal_actions() const; + void _internal_add_actions(int32_t value); + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + _internal_mutable_actions(); + public: + int32_t actions(int index) const; + void set_actions(int index, int32_t value); + void add_actions(int32_t value); + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + actions() const; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + mutable_actions(); + + // optional int32 identity_type = 2; + bool has_identity_type() const; + private: + bool _internal_has_identity_type() const; + public: + void clear_identity_type(); + int32_t identity_type() const; + void set_identity_type(int32_t value); + private: + int32_t _internal_identity_type() const; + void _internal_set_identity_type(int32_t value); + public: + + // optional bool uwb_enable = 3; + bool has_uwb_enable() const; + private: + bool _internal_has_uwb_enable() const; + public: + void clear_uwb_enable(); + bool uwb_enable() const; + void set_uwb_enable(bool value); + private: + bool _internal_uwb_enable() const; + void _internal_set_uwb_enable(bool value); + public: + + // optional int64 device_unique_id = 4; + bool has_device_unique_id() const; + private: + bool _internal_has_device_unique_id() const; + public: + void clear_device_unique_id(); + int64_t device_unique_id() const; + void set_device_unique_id(int64_t value); + private: + int64_t _internal_device_unique_id() const; + void _internal_set_device_unique_id(int64_t value); + public: + + // @@protoc_insertion_point(class_scope:nearby.presence.ConnectionInitFrame) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > actions_; + mutable std::atomic _actions_cached_byte_size_; + int32_t identity_type_; + bool uwb_enable_; + int64_t device_unique_id_; + friend struct ::TableStruct_presence_2fproto_2fpresence_5fframe_2eproto; +}; +// ------------------------------------------------------------------- + +class UwbControleeCapabilities final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:nearby.presence.UwbControleeCapabilities) */ { + public: + inline UwbControleeCapabilities() : UwbControleeCapabilities(nullptr) {} + ~UwbControleeCapabilities() override; + explicit constexpr UwbControleeCapabilities(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + UwbControleeCapabilities(const UwbControleeCapabilities& from); + UwbControleeCapabilities(UwbControleeCapabilities&& from) noexcept + : UwbControleeCapabilities() { + *this = ::std::move(from); + } + + inline UwbControleeCapabilities& operator=(const UwbControleeCapabilities& from) { + CopyFrom(from); + return *this; + } + inline UwbControleeCapabilities& operator=(UwbControleeCapabilities&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance); + } + inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const UwbControleeCapabilities& default_instance() { + return *internal_default_instance(); + } + static inline const UwbControleeCapabilities* internal_default_instance() { + return reinterpret_cast( + &_UwbControleeCapabilities_default_instance_); + } + static constexpr int kIndexInFileMessages = + 4; + + friend void swap(UwbControleeCapabilities& a, UwbControleeCapabilities& b) { + a.Swap(&b); + } + inline void Swap(UwbControleeCapabilities* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(UwbControleeCapabilities* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + UwbControleeCapabilities* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const UwbControleeCapabilities& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom(const UwbControleeCapabilities& from); + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(UwbControleeCapabilities* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "nearby.presence.UwbControleeCapabilities"; + } + protected: + explicit UwbControleeCapabilities(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kSupportedConfigIdsFieldNumber = 2, + kSupportedChannelsFieldNumber = 3, + kSupportedNtfConfigsFieldNumber = 13, + kSupportedSlotDurationsFieldNumber = 15, + kSupportedRangingUpdateRatesFieldNumber = 16, + kMultiChipInfoFieldNumber = 18, + kControleeAddressFieldNumber = 1, + kSubSessionIdFieldNumber = 5, + kSubSessionKeyFieldNumber = 6, + kDeviceUniqueIdFieldNumber = 8, + kMinRangingIntervalMsFieldNumber = 4, + kRangingDisabledFieldNumber = 7, + kIsElevationSupportedFieldNumber = 11, + kIsRangingIntervalReconfigureSupportedFieldNumber = 14, + kChipCountFieldNumber = 17, + kIsDistanceSupportedFieldNumber = 9, + kIsAzimuthSupportedFieldNumber = 10, + kMinSlotDurationMsFieldNumber = 12, + }; + // repeated int32 supported_config_ids = 2 [packed = true]; + int supported_config_ids_size() const; + private: + int _internal_supported_config_ids_size() const; + public: + void clear_supported_config_ids(); + private: + int32_t _internal_supported_config_ids(int index) const; + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + _internal_supported_config_ids() const; + void _internal_add_supported_config_ids(int32_t value); + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + _internal_mutable_supported_config_ids(); + public: + int32_t supported_config_ids(int index) const; + void set_supported_config_ids(int index, int32_t value); + void add_supported_config_ids(int32_t value); + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + supported_config_ids() const; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + mutable_supported_config_ids(); + + // repeated int32 supported_channels = 3 [packed = true]; + int supported_channels_size() const; + private: + int _internal_supported_channels_size() const; + public: + void clear_supported_channels(); + private: + int32_t _internal_supported_channels(int index) const; + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + _internal_supported_channels() const; + void _internal_add_supported_channels(int32_t value); + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + _internal_mutable_supported_channels(); + public: + int32_t supported_channels(int index) const; + void set_supported_channels(int index, int32_t value); + void add_supported_channels(int32_t value); + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + supported_channels() const; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + mutable_supported_channels(); + + // repeated int32 supported_ntf_configs = 13 [packed = true]; + int supported_ntf_configs_size() const; + private: + int _internal_supported_ntf_configs_size() const; + public: + void clear_supported_ntf_configs(); + private: + int32_t _internal_supported_ntf_configs(int index) const; + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + _internal_supported_ntf_configs() const; + void _internal_add_supported_ntf_configs(int32_t value); + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + _internal_mutable_supported_ntf_configs(); + public: + int32_t supported_ntf_configs(int index) const; + void set_supported_ntf_configs(int index, int32_t value); + void add_supported_ntf_configs(int32_t value); + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + supported_ntf_configs() const; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + mutable_supported_ntf_configs(); + + // repeated int32 supported_slot_durations = 15 [packed = true]; + int supported_slot_durations_size() const; + private: + int _internal_supported_slot_durations_size() const; + public: + void clear_supported_slot_durations(); + private: + int32_t _internal_supported_slot_durations(int index) const; + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + _internal_supported_slot_durations() const; + void _internal_add_supported_slot_durations(int32_t value); + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + _internal_mutable_supported_slot_durations(); + public: + int32_t supported_slot_durations(int index) const; + void set_supported_slot_durations(int index, int32_t value); + void add_supported_slot_durations(int32_t value); + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + supported_slot_durations() const; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + mutable_supported_slot_durations(); + + // repeated int32 supported_ranging_update_rates = 16 [packed = true]; + int supported_ranging_update_rates_size() const; + private: + int _internal_supported_ranging_update_rates_size() const; + public: + void clear_supported_ranging_update_rates(); + private: + int32_t _internal_supported_ranging_update_rates(int index) const; + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + _internal_supported_ranging_update_rates() const; + void _internal_add_supported_ranging_update_rates(int32_t value); + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + _internal_mutable_supported_ranging_update_rates(); + public: + int32_t supported_ranging_update_rates(int index) const; + void set_supported_ranging_update_rates(int index, int32_t value); + void add_supported_ranging_update_rates(int32_t value); + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& + supported_ranging_update_rates() const; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* + mutable_supported_ranging_update_rates(); + + // repeated .nearby.presence.UwbMultiChipInfo multi_chip_info = 18; + int multi_chip_info_size() const; + private: + int _internal_multi_chip_info_size() const; + public: + void clear_multi_chip_info(); + ::nearby::presence::UwbMultiChipInfo* mutable_multi_chip_info(int index); + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::nearby::presence::UwbMultiChipInfo >* + mutable_multi_chip_info(); + private: + const ::nearby::presence::UwbMultiChipInfo& _internal_multi_chip_info(int index) const; + ::nearby::presence::UwbMultiChipInfo* _internal_add_multi_chip_info(); + public: + const ::nearby::presence::UwbMultiChipInfo& multi_chip_info(int index) const; + ::nearby::presence::UwbMultiChipInfo* add_multi_chip_info(); + const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::nearby::presence::UwbMultiChipInfo >& + multi_chip_info() const; + + // optional bytes controlee_address = 1; + bool has_controlee_address() const; + private: + bool _internal_has_controlee_address() const; + public: + void clear_controlee_address(); + const std::string& controlee_address() const; + template + void set_controlee_address(ArgT0&& arg0, ArgT... args); + std::string* mutable_controlee_address(); + PROTOBUF_NODISCARD std::string* release_controlee_address(); + void set_allocated_controlee_address(std::string* controlee_address); + private: + const std::string& _internal_controlee_address() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_controlee_address(const std::string& value); + std::string* _internal_mutable_controlee_address(); + public: + + // optional bytes sub_session_id = 5; + bool has_sub_session_id() const; + private: + bool _internal_has_sub_session_id() const; + public: + void clear_sub_session_id(); + const std::string& sub_session_id() const; + template + void set_sub_session_id(ArgT0&& arg0, ArgT... args); + std::string* mutable_sub_session_id(); + PROTOBUF_NODISCARD std::string* release_sub_session_id(); + void set_allocated_sub_session_id(std::string* sub_session_id); + private: + const std::string& _internal_sub_session_id() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_sub_session_id(const std::string& value); + std::string* _internal_mutable_sub_session_id(); + public: + + // optional bytes sub_session_key = 6; + bool has_sub_session_key() const; + private: + bool _internal_has_sub_session_key() const; + public: + void clear_sub_session_key(); + const std::string& sub_session_key() const; + template + void set_sub_session_key(ArgT0&& arg0, ArgT... args); + std::string* mutable_sub_session_key(); + PROTOBUF_NODISCARD std::string* release_sub_session_key(); + void set_allocated_sub_session_key(std::string* sub_session_key); + private: + const std::string& _internal_sub_session_key() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_sub_session_key(const std::string& value); + std::string* _internal_mutable_sub_session_key(); + public: + + // optional int64 device_unique_id = 8; + bool has_device_unique_id() const; + private: + bool _internal_has_device_unique_id() const; + public: + void clear_device_unique_id(); + int64_t device_unique_id() const; + void set_device_unique_id(int64_t value); + private: + int64_t _internal_device_unique_id() const; + void _internal_set_device_unique_id(int64_t value); + public: + + // optional int32 min_ranging_interval_ms = 4; + bool has_min_ranging_interval_ms() const; + private: + bool _internal_has_min_ranging_interval_ms() const; + public: + void clear_min_ranging_interval_ms(); + int32_t min_ranging_interval_ms() const; + void set_min_ranging_interval_ms(int32_t value); + private: + int32_t _internal_min_ranging_interval_ms() const; + void _internal_set_min_ranging_interval_ms(int32_t value); + public: + + // optional bool ranging_disabled = 7; + bool has_ranging_disabled() const; + private: + bool _internal_has_ranging_disabled() const; + public: + void clear_ranging_disabled(); + bool ranging_disabled() const; + void set_ranging_disabled(bool value); + private: + bool _internal_ranging_disabled() const; + void _internal_set_ranging_disabled(bool value); + public: + + // optional bool is_elevation_supported = 11 [default = false]; + bool has_is_elevation_supported() const; + private: + bool _internal_has_is_elevation_supported() const; + public: + void clear_is_elevation_supported(); + bool is_elevation_supported() const; + void set_is_elevation_supported(bool value); + private: + bool _internal_is_elevation_supported() const; + void _internal_set_is_elevation_supported(bool value); + public: + + // optional bool is_ranging_interval_reconfigure_supported = 14 [default = false]; + bool has_is_ranging_interval_reconfigure_supported() const; + private: + bool _internal_has_is_ranging_interval_reconfigure_supported() const; + public: + void clear_is_ranging_interval_reconfigure_supported(); + bool is_ranging_interval_reconfigure_supported() const; + void set_is_ranging_interval_reconfigure_supported(bool value); + private: + bool _internal_is_ranging_interval_reconfigure_supported() const; + void _internal_set_is_ranging_interval_reconfigure_supported(bool value); + public: + + // optional int32 chip_count = 17 [default = 1]; + bool has_chip_count() const; + private: + bool _internal_has_chip_count() const; + public: + void clear_chip_count(); + int32_t chip_count() const; + void set_chip_count(int32_t value); + private: + int32_t _internal_chip_count() const; + void _internal_set_chip_count(int32_t value); + public: + + // optional bool is_distance_supported = 9 [default = true]; + bool has_is_distance_supported() const; + private: + bool _internal_has_is_distance_supported() const; + public: + void clear_is_distance_supported(); + bool is_distance_supported() const; + void set_is_distance_supported(bool value); + private: + bool _internal_is_distance_supported() const; + void _internal_set_is_distance_supported(bool value); + public: + + // optional bool is_azimuth_supported = 10 [default = true]; + bool has_is_azimuth_supported() const; + private: + bool _internal_has_is_azimuth_supported() const; + public: + void clear_is_azimuth_supported(); + bool is_azimuth_supported() const; + void set_is_azimuth_supported(bool value); + private: + bool _internal_is_azimuth_supported() const; + void _internal_set_is_azimuth_supported(bool value); + public: + + // optional float min_slot_duration_ms = 12 [default = 2]; + bool has_min_slot_duration_ms() const; + private: + bool _internal_has_min_slot_duration_ms() const; + public: + void clear_min_slot_duration_ms(); + float min_slot_duration_ms() const; + void set_min_slot_duration_ms(float value); + private: + float _internal_min_slot_duration_ms() const; + void _internal_set_min_slot_duration_ms(float value); + public: + + // @@protoc_insertion_point(class_scope:nearby.presence.UwbControleeCapabilities) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > supported_config_ids_; + mutable std::atomic _supported_config_ids_cached_byte_size_; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > supported_channels_; + mutable std::atomic _supported_channels_cached_byte_size_; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > supported_ntf_configs_; + mutable std::atomic _supported_ntf_configs_cached_byte_size_; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > supported_slot_durations_; + mutable std::atomic _supported_slot_durations_cached_byte_size_; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t > supported_ranging_update_rates_; + mutable std::atomic _supported_ranging_update_rates_cached_byte_size_; + ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::nearby::presence::UwbMultiChipInfo > multi_chip_info_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr controlee_address_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr sub_session_id_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr sub_session_key_; + int64_t device_unique_id_; + int32_t min_ranging_interval_ms_; + bool ranging_disabled_; + bool is_elevation_supported_; + bool is_ranging_interval_reconfigure_supported_; + int32_t chip_count_; + bool is_distance_supported_; + bool is_azimuth_supported_; + float min_slot_duration_ms_; + friend struct ::TableStruct_presence_2fproto_2fpresence_5fframe_2eproto; +}; +// ------------------------------------------------------------------- + +class UwbMultiChipInfo final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:nearby.presence.UwbMultiChipInfo) */ { + public: + inline UwbMultiChipInfo() : UwbMultiChipInfo(nullptr) {} + ~UwbMultiChipInfo() override; + explicit constexpr UwbMultiChipInfo(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + UwbMultiChipInfo(const UwbMultiChipInfo& from); + UwbMultiChipInfo(UwbMultiChipInfo&& from) noexcept + : UwbMultiChipInfo() { + *this = ::std::move(from); + } + + inline UwbMultiChipInfo& operator=(const UwbMultiChipInfo& from) { + CopyFrom(from); + return *this; + } + inline UwbMultiChipInfo& operator=(UwbMultiChipInfo&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance); + } + inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const UwbMultiChipInfo& default_instance() { + return *internal_default_instance(); + } + static inline const UwbMultiChipInfo* internal_default_instance() { + return reinterpret_cast( + &_UwbMultiChipInfo_default_instance_); + } + static constexpr int kIndexInFileMessages = + 5; + + friend void swap(UwbMultiChipInfo& a, UwbMultiChipInfo& b) { + a.Swap(&b); + } + inline void Swap(UwbMultiChipInfo* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(UwbMultiChipInfo* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + UwbMultiChipInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const UwbMultiChipInfo& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom(const UwbMultiChipInfo& from); + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(UwbMultiChipInfo* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "nearby.presence.UwbMultiChipInfo"; + } + protected: + explicit UwbMultiChipInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kControleeAddressFieldNumber = 1, + kChipIdFieldNumber = 2, + }; + // optional bytes controlee_address = 1; + bool has_controlee_address() const; + private: + bool _internal_has_controlee_address() const; + public: + void clear_controlee_address(); + const std::string& controlee_address() const; + template + void set_controlee_address(ArgT0&& arg0, ArgT... args); + std::string* mutable_controlee_address(); + PROTOBUF_NODISCARD std::string* release_controlee_address(); + void set_allocated_controlee_address(std::string* controlee_address); + private: + const std::string& _internal_controlee_address() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_controlee_address(const std::string& value); + std::string* _internal_mutable_controlee_address(); + public: + + // optional string chip_id = 2; + bool has_chip_id() const; + private: + bool _internal_has_chip_id() const; + public: + void clear_chip_id(); + const std::string& chip_id() const; + template + void set_chip_id(ArgT0&& arg0, ArgT... args); + std::string* mutable_chip_id(); + PROTOBUF_NODISCARD std::string* release_chip_id(); + void set_allocated_chip_id(std::string* chip_id); + private: + const std::string& _internal_chip_id() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_chip_id(const std::string& value); + std::string* _internal_mutable_chip_id(); + public: + + // @@protoc_insertion_point(class_scope:nearby.presence.UwbMultiChipInfo) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr controlee_address_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr chip_id_; + friend struct ::TableStruct_presence_2fproto_2fpresence_5fframe_2eproto; +}; +// ------------------------------------------------------------------- + +class UwbConnectionInfo final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:nearby.presence.UwbConnectionInfo) */ { + public: + inline UwbConnectionInfo() : UwbConnectionInfo(nullptr) {} + ~UwbConnectionInfo() override; + explicit constexpr UwbConnectionInfo(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + UwbConnectionInfo(const UwbConnectionInfo& from); + UwbConnectionInfo(UwbConnectionInfo&& from) noexcept + : UwbConnectionInfo() { + *this = ::std::move(from); + } + + inline UwbConnectionInfo& operator=(const UwbConnectionInfo& from) { + CopyFrom(from); + return *this; + } + inline UwbConnectionInfo& operator=(UwbConnectionInfo&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance); + } + inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const UwbConnectionInfo& default_instance() { + return *internal_default_instance(); + } + static inline const UwbConnectionInfo* internal_default_instance() { + return reinterpret_cast( + &_UwbConnectionInfo_default_instance_); + } + static constexpr int kIndexInFileMessages = + 6; + + friend void swap(UwbConnectionInfo& a, UwbConnectionInfo& b) { + a.Swap(&b); + } + inline void Swap(UwbConnectionInfo* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(UwbConnectionInfo* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + UwbConnectionInfo* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const UwbConnectionInfo& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom(const UwbConnectionInfo& from); + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(UwbConnectionInfo* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "nearby.presence.UwbConnectionInfo"; + } + protected: + explicit UwbConnectionInfo(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kControllerAddressFieldNumber = 1, + kVendorIdFieldNumber = 7, + kStaticStsIvFieldNumber = 8, + kSessionKeyFieldNumber = 9, + kChannelFieldNumber = 2, + kPreambleIndexFieldNumber = 3, + kConfigIdFieldNumber = 4, + kRangingIntervalMsFieldNumber = 5, + kSessionIdFieldNumber = 6, + kRangingDisabledFieldNumber = 10, + }; + // optional bytes controller_address = 1; + bool has_controller_address() const; + private: + bool _internal_has_controller_address() const; + public: + void clear_controller_address(); + const std::string& controller_address() const; + template + void set_controller_address(ArgT0&& arg0, ArgT... args); + std::string* mutable_controller_address(); + PROTOBUF_NODISCARD std::string* release_controller_address(); + void set_allocated_controller_address(std::string* controller_address); + private: + const std::string& _internal_controller_address() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_controller_address(const std::string& value); + std::string* _internal_mutable_controller_address(); + public: + + // optional bytes vendor_id = 7; + bool has_vendor_id() const; + private: + bool _internal_has_vendor_id() const; + public: + void clear_vendor_id(); + const std::string& vendor_id() const; + template + void set_vendor_id(ArgT0&& arg0, ArgT... args); + std::string* mutable_vendor_id(); + PROTOBUF_NODISCARD std::string* release_vendor_id(); + void set_allocated_vendor_id(std::string* vendor_id); + private: + const std::string& _internal_vendor_id() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_vendor_id(const std::string& value); + std::string* _internal_mutable_vendor_id(); + public: + + // optional bytes static_sts_iv = 8; + bool has_static_sts_iv() const; + private: + bool _internal_has_static_sts_iv() const; + public: + void clear_static_sts_iv(); + const std::string& static_sts_iv() const; + template + void set_static_sts_iv(ArgT0&& arg0, ArgT... args); + std::string* mutable_static_sts_iv(); + PROTOBUF_NODISCARD std::string* release_static_sts_iv(); + void set_allocated_static_sts_iv(std::string* static_sts_iv); + private: + const std::string& _internal_static_sts_iv() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_static_sts_iv(const std::string& value); + std::string* _internal_mutable_static_sts_iv(); + public: + + // optional bytes session_key = 9; + bool has_session_key() const; + private: + bool _internal_has_session_key() const; + public: + void clear_session_key(); + const std::string& session_key() const; + template + void set_session_key(ArgT0&& arg0, ArgT... args); + std::string* mutable_session_key(); + PROTOBUF_NODISCARD std::string* release_session_key(); + void set_allocated_session_key(std::string* session_key); + private: + const std::string& _internal_session_key() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_session_key(const std::string& value); + std::string* _internal_mutable_session_key(); + public: + + // optional int32 channel = 2; + bool has_channel() const; + private: + bool _internal_has_channel() const; + public: + void clear_channel(); + int32_t channel() const; + void set_channel(int32_t value); + private: + int32_t _internal_channel() const; + void _internal_set_channel(int32_t value); + public: + + // optional int32 preamble_index = 3; + bool has_preamble_index() const; + private: + bool _internal_has_preamble_index() const; + public: + void clear_preamble_index(); + int32_t preamble_index() const; + void set_preamble_index(int32_t value); + private: + int32_t _internal_preamble_index() const; + void _internal_set_preamble_index(int32_t value); + public: + + // optional int32 config_id = 4; + bool has_config_id() const; + private: + bool _internal_has_config_id() const; + public: + void clear_config_id(); + int32_t config_id() const; + void set_config_id(int32_t value); + private: + int32_t _internal_config_id() const; + void _internal_set_config_id(int32_t value); + public: + + // optional int32 ranging_interval_ms = 5; + bool has_ranging_interval_ms() const; + private: + bool _internal_has_ranging_interval_ms() const; + public: + void clear_ranging_interval_ms(); + int32_t ranging_interval_ms() const; + void set_ranging_interval_ms(int32_t value); + private: + int32_t _internal_ranging_interval_ms() const; + void _internal_set_ranging_interval_ms(int32_t value); + public: + + // optional int32 session_id = 6; + bool has_session_id() const; + private: + bool _internal_has_session_id() const; + public: + void clear_session_id(); + int32_t session_id() const; + void set_session_id(int32_t value); + private: + int32_t _internal_session_id() const; + void _internal_set_session_id(int32_t value); + public: + + // optional bool ranging_disabled = 10; + bool has_ranging_disabled() const; + private: + bool _internal_has_ranging_disabled() const; + public: + void clear_ranging_disabled(); + bool ranging_disabled() const; + void set_ranging_disabled(bool value); + private: + bool _internal_ranging_disabled() const; + void _internal_set_ranging_disabled(bool value); + public: + + // @@protoc_insertion_point(class_scope:nearby.presence.UwbConnectionInfo) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr controller_address_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr vendor_id_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr static_sts_iv_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr session_key_; + int32_t channel_; + int32_t preamble_index_; + int32_t config_id_; + int32_t ranging_interval_ms_; + int32_t session_id_; + bool ranging_disabled_; + friend struct ::TableStruct_presence_2fproto_2fpresence_5fframe_2eproto; +}; +// ------------------------------------------------------------------- + +class ControlFrame final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:nearby.presence.ControlFrame) */ { + public: + inline ControlFrame() : ControlFrame(nullptr) {} + ~ControlFrame() override; + explicit constexpr ControlFrame(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + ControlFrame(const ControlFrame& from); + ControlFrame(ControlFrame&& from) noexcept + : ControlFrame() { + *this = ::std::move(from); + } + + inline ControlFrame& operator=(const ControlFrame& from) { + CopyFrom(from); + return *this; + } + inline ControlFrame& operator=(ControlFrame&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance); + } + inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const ControlFrame& default_instance() { + return *internal_default_instance(); + } + static inline const ControlFrame* internal_default_instance() { + return reinterpret_cast( + &_ControlFrame_default_instance_); + } + static constexpr int kIndexInFileMessages = + 7; + + friend void swap(ControlFrame& a, ControlFrame& b) { + a.Swap(&b); + } + inline void Swap(ControlFrame* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(ControlFrame* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + ControlFrame* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const ControlFrame& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom(const ControlFrame& from); + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(ControlFrame* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "nearby.presence.ControlFrame"; + } + protected: + explicit ControlFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + typedef ControlFrame_ControlType ControlType; + static constexpr ControlType UNKNOWN_TYPE = + ControlFrame_ControlType_UNKNOWN_TYPE; + static constexpr ControlType KEEP_ALIVE = + ControlFrame_ControlType_KEEP_ALIVE; + static constexpr ControlType DISCONNECT = + ControlFrame_ControlType_DISCONNECT; + static inline bool ControlType_IsValid(int value) { + return ControlFrame_ControlType_IsValid(value); + } + static constexpr ControlType ControlType_MIN = + ControlFrame_ControlType_ControlType_MIN; + static constexpr ControlType ControlType_MAX = + ControlFrame_ControlType_ControlType_MAX; + static constexpr int ControlType_ARRAYSIZE = + ControlFrame_ControlType_ControlType_ARRAYSIZE; + static inline const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* + ControlType_descriptor() { + return ControlFrame_ControlType_descriptor(); + } + template + static inline const std::string& ControlType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function ControlType_Name."); + return ControlFrame_ControlType_Name(enum_t_value); + } + static inline bool ControlType_Parse(::PROTOBUF_NAMESPACE_ID::ConstStringParam name, + ControlType* value) { + return ControlFrame_ControlType_Parse(name, value); + } + + // accessors ------------------------------------------------------- + + enum : int { + kTypeFieldNumber = 1, + }; + // optional .nearby.presence.ControlFrame.ControlType type = 1; + bool has_type() const; + private: + bool _internal_has_type() const; + public: + void clear_type(); + ::nearby::presence::ControlFrame_ControlType type() const; + void set_type(::nearby::presence::ControlFrame_ControlType value); + private: + ::nearby::presence::ControlFrame_ControlType _internal_type() const; + void _internal_set_type(::nearby::presence::ControlFrame_ControlType value); + public: + + // @@protoc_insertion_point(class_scope:nearby.presence.ControlFrame) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + int type_; + friend struct ::TableStruct_presence_2fproto_2fpresence_5fframe_2eproto; +}; +// ------------------------------------------------------------------- + +class PresenceAuthenticationFrame final : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:nearby.presence.PresenceAuthenticationFrame) */ { + public: + inline PresenceAuthenticationFrame() : PresenceAuthenticationFrame(nullptr) {} + ~PresenceAuthenticationFrame() override; + explicit constexpr PresenceAuthenticationFrame(::PROTOBUF_NAMESPACE_ID::internal::ConstantInitialized); + + PresenceAuthenticationFrame(const PresenceAuthenticationFrame& from); + PresenceAuthenticationFrame(PresenceAuthenticationFrame&& from) noexcept + : PresenceAuthenticationFrame() { + *this = ::std::move(from); + } + + inline PresenceAuthenticationFrame& operator=(const PresenceAuthenticationFrame& from) { + CopyFrom(from); + return *this; + } + inline PresenceAuthenticationFrame& operator=(PresenceAuthenticationFrame&& from) noexcept { + if (this == &from) return *this; + if (GetOwningArena() == from.GetOwningArena() + #ifdef PROTOBUF_FORCE_COPY_IN_MOVE + && GetOwningArena() != nullptr + #endif // !PROTOBUF_FORCE_COPY_IN_MOVE + ) { + InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + inline const ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance); + } + inline ::PROTOBUF_NAMESPACE_ID::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return default_instance().GetMetadata().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return default_instance().GetMetadata().reflection; + } + static const PresenceAuthenticationFrame& default_instance() { + return *internal_default_instance(); + } + static inline const PresenceAuthenticationFrame* internal_default_instance() { + return reinterpret_cast( + &_PresenceAuthenticationFrame_default_instance_); + } + static constexpr int kIndexInFileMessages = + 8; + + friend void swap(PresenceAuthenticationFrame& a, PresenceAuthenticationFrame& b) { + a.Swap(&b); + } + inline void Swap(PresenceAuthenticationFrame* other) { + if (other == this) return; + #ifdef PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() != nullptr && + GetOwningArena() == other->GetOwningArena()) { + #else // PROTOBUF_FORCE_COPY_IN_SWAP + if (GetOwningArena() == other->GetOwningArena()) { + #endif // !PROTOBUF_FORCE_COPY_IN_SWAP + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(PresenceAuthenticationFrame* other) { + if (other == this) return; + GOOGLE_DCHECK(GetOwningArena() == other->GetOwningArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + PresenceAuthenticationFrame* New(::PROTOBUF_NAMESPACE_ID::Arena* arena = nullptr) const final { + return CreateMaybeMessage(arena); + } + using ::PROTOBUF_NAMESPACE_ID::Message::CopyFrom; + void CopyFrom(const PresenceAuthenticationFrame& from); + using ::PROTOBUF_NAMESPACE_ID::Message::MergeFrom; + void MergeFrom(const PresenceAuthenticationFrame& from); + private: + static void MergeImpl(::PROTOBUF_NAMESPACE_ID::Message* to, const ::PROTOBUF_NAMESPACE_ID::Message& from); + public: + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + uint8_t* _InternalSerialize( + uint8_t* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(PresenceAuthenticationFrame* other); + + private: + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "nearby.presence.PresenceAuthenticationFrame"; + } + protected: + explicit PresenceAuthenticationFrame(::PROTOBUF_NAMESPACE_ID::Arena* arena, + bool is_message_owned = false); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + static const ClassData _class_data_; + const ::PROTOBUF_NAMESPACE_ID::Message::ClassData*GetClassData() const final; + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kPrivateKeySignatureFieldNumber = 2, + kSharedCredentialIdHashFieldNumber = 3, + kCredentialIdHashFieldNumber = 4, + kVersionFieldNumber = 1, + }; + // optional bytes private_key_signature = 2; + bool has_private_key_signature() const; + private: + bool _internal_has_private_key_signature() const; + public: + void clear_private_key_signature(); + const std::string& private_key_signature() const; + template + void set_private_key_signature(ArgT0&& arg0, ArgT... args); + std::string* mutable_private_key_signature(); + PROTOBUF_NODISCARD std::string* release_private_key_signature(); + void set_allocated_private_key_signature(std::string* private_key_signature); + private: + const std::string& _internal_private_key_signature() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_private_key_signature(const std::string& value); + std::string* _internal_mutable_private_key_signature(); + public: + + // optional bytes shared_credential_id_hash = 3; + bool has_shared_credential_id_hash() const; + private: + bool _internal_has_shared_credential_id_hash() const; + public: + void clear_shared_credential_id_hash(); + const std::string& shared_credential_id_hash() const; + template + void set_shared_credential_id_hash(ArgT0&& arg0, ArgT... args); + std::string* mutable_shared_credential_id_hash(); + PROTOBUF_NODISCARD std::string* release_shared_credential_id_hash(); + void set_allocated_shared_credential_id_hash(std::string* shared_credential_id_hash); + private: + const std::string& _internal_shared_credential_id_hash() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_shared_credential_id_hash(const std::string& value); + std::string* _internal_mutable_shared_credential_id_hash(); + public: + + // optional bytes credential_id_hash = 4 [deprecated = true]; + PROTOBUF_DEPRECATED bool has_credential_id_hash() const; + private: + bool _internal_has_credential_id_hash() const; + public: + PROTOBUF_DEPRECATED void clear_credential_id_hash(); + PROTOBUF_DEPRECATED const std::string& credential_id_hash() const; + template + PROTOBUF_DEPRECATED void set_credential_id_hash(ArgT0&& arg0, ArgT... args); + PROTOBUF_DEPRECATED std::string* mutable_credential_id_hash(); + PROTOBUF_NODISCARD PROTOBUF_DEPRECATED std::string* release_credential_id_hash(); + PROTOBUF_DEPRECATED void set_allocated_credential_id_hash(std::string* credential_id_hash); + private: + const std::string& _internal_credential_id_hash() const; + inline PROTOBUF_ALWAYS_INLINE void _internal_set_credential_id_hash(const std::string& value); + std::string* _internal_mutable_credential_id_hash(); + public: + + // optional int32 version = 1; + bool has_version() const; + private: + bool _internal_has_version() const; + public: + void clear_version(); + int32_t version() const; + void set_version(int32_t value); + private: + int32_t _internal_version() const; + void _internal_set_version(int32_t value); + public: + + // @@protoc_insertion_point(class_scope:nearby.presence.PresenceAuthenticationFrame) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::HasBits<1> _has_bits_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr private_key_signature_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr shared_credential_id_hash_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr credential_id_hash_; + int32_t version_; + friend struct ::TableStruct_presence_2fproto_2fpresence_5fframe_2eproto; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// PresenceFrame + +// optional .nearby.presence.V1Frame v1_frame = 1; +inline bool PresenceFrame::_internal_has_v1_frame() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + PROTOBUF_ASSUME(!value || v1_frame_ != nullptr); + return value; +} +inline bool PresenceFrame::has_v1_frame() const { + return _internal_has_v1_frame(); +} +inline void PresenceFrame::clear_v1_frame() { + if (v1_frame_ != nullptr) v1_frame_->Clear(); + _has_bits_[0] &= ~0x00000001u; +} +inline const ::nearby::presence::V1Frame& PresenceFrame::_internal_v1_frame() const { + const ::nearby::presence::V1Frame* p = v1_frame_; + return p != nullptr ? *p : reinterpret_cast( + ::nearby::presence::_V1Frame_default_instance_); +} +inline const ::nearby::presence::V1Frame& PresenceFrame::v1_frame() const { + // @@protoc_insertion_point(field_get:nearby.presence.PresenceFrame.v1_frame) + return _internal_v1_frame(); +} +inline void PresenceFrame::unsafe_arena_set_allocated_v1_frame( + ::nearby::presence::V1Frame* v1_frame) { + if (GetArenaForAllocation() == nullptr) { + delete reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(v1_frame_); + } + v1_frame_ = v1_frame; + if (v1_frame) { + _has_bits_[0] |= 0x00000001u; + } else { + _has_bits_[0] &= ~0x00000001u; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:nearby.presence.PresenceFrame.v1_frame) +} +inline ::nearby::presence::V1Frame* PresenceFrame::release_v1_frame() { + _has_bits_[0] &= ~0x00000001u; + ::nearby::presence::V1Frame* temp = v1_frame_; + v1_frame_ = nullptr; +#ifdef PROTOBUF_FORCE_COPY_IN_RELEASE + auto* old = reinterpret_cast<::PROTOBUF_NAMESPACE_ID::MessageLite*>(temp); + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + if (GetArenaForAllocation() == nullptr) { delete old; } +#else // PROTOBUF_FORCE_COPY_IN_RELEASE + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } +#endif // !PROTOBUF_FORCE_COPY_IN_RELEASE + return temp; +} +inline ::nearby::presence::V1Frame* PresenceFrame::unsafe_arena_release_v1_frame() { + // @@protoc_insertion_point(field_release:nearby.presence.PresenceFrame.v1_frame) + _has_bits_[0] &= ~0x00000001u; + ::nearby::presence::V1Frame* temp = v1_frame_; + v1_frame_ = nullptr; + return temp; +} +inline ::nearby::presence::V1Frame* PresenceFrame::_internal_mutable_v1_frame() { + _has_bits_[0] |= 0x00000001u; + if (v1_frame_ == nullptr) { + auto* p = CreateMaybeMessage<::nearby::presence::V1Frame>(GetArenaForAllocation()); + v1_frame_ = p; + } + return v1_frame_; +} +inline ::nearby::presence::V1Frame* PresenceFrame::mutable_v1_frame() { + ::nearby::presence::V1Frame* _msg = _internal_mutable_v1_frame(); + // @@protoc_insertion_point(field_mutable:nearby.presence.PresenceFrame.v1_frame) + return _msg; +} +inline void PresenceFrame::set_allocated_v1_frame(::nearby::presence::V1Frame* v1_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* message_arena = GetArenaForAllocation(); + if (message_arena == nullptr) { + delete v1_frame_; + } + if (v1_frame) { + ::PROTOBUF_NAMESPACE_ID::Arena* submessage_arena = + ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper<::nearby::presence::V1Frame>::GetOwningArena(v1_frame); + if (message_arena != submessage_arena) { + v1_frame = ::PROTOBUF_NAMESPACE_ID::internal::GetOwnedMessage( + message_arena, v1_frame, submessage_arena); + } + _has_bits_[0] |= 0x00000001u; + } else { + _has_bits_[0] &= ~0x00000001u; + } + v1_frame_ = v1_frame; + // @@protoc_insertion_point(field_set_allocated:nearby.presence.PresenceFrame.v1_frame) +} + +// ------------------------------------------------------------------- + +// V1Frame + +// .nearby.presence.ControlFrame control_frame = 1; +inline bool V1Frame::_internal_has_control_frame() const { + return Message_case() == kControlFrame; +} +inline bool V1Frame::has_control_frame() const { + return _internal_has_control_frame(); +} +inline void V1Frame::set_has_control_frame() { + _oneof_case_[0] = kControlFrame; +} +inline void V1Frame::clear_control_frame() { + if (_internal_has_control_frame()) { + if (GetArenaForAllocation() == nullptr) { + delete Message_.control_frame_; + } + clear_has_Message(); + } +} +inline ::nearby::presence::ControlFrame* V1Frame::release_control_frame() { + // @@protoc_insertion_point(field_release:nearby.presence.V1Frame.control_frame) + if (_internal_has_control_frame()) { + clear_has_Message(); + ::nearby::presence::ControlFrame* temp = Message_.control_frame_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + Message_.control_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::nearby::presence::ControlFrame& V1Frame::_internal_control_frame() const { + return _internal_has_control_frame() + ? *Message_.control_frame_ + : reinterpret_cast< ::nearby::presence::ControlFrame&>(::nearby::presence::_ControlFrame_default_instance_); +} +inline const ::nearby::presence::ControlFrame& V1Frame::control_frame() const { + // @@protoc_insertion_point(field_get:nearby.presence.V1Frame.control_frame) + return _internal_control_frame(); +} +inline ::nearby::presence::ControlFrame* V1Frame::unsafe_arena_release_control_frame() { + // @@protoc_insertion_point(field_unsafe_arena_release:nearby.presence.V1Frame.control_frame) + if (_internal_has_control_frame()) { + clear_has_Message(); + ::nearby::presence::ControlFrame* temp = Message_.control_frame_; + Message_.control_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void V1Frame::unsafe_arena_set_allocated_control_frame(::nearby::presence::ControlFrame* control_frame) { + clear_Message(); + if (control_frame) { + set_has_control_frame(); + Message_.control_frame_ = control_frame; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:nearby.presence.V1Frame.control_frame) +} +inline ::nearby::presence::ControlFrame* V1Frame::_internal_mutable_control_frame() { + if (!_internal_has_control_frame()) { + clear_Message(); + set_has_control_frame(); + Message_.control_frame_ = CreateMaybeMessage< ::nearby::presence::ControlFrame >(GetArenaForAllocation()); + } + return Message_.control_frame_; +} +inline ::nearby::presence::ControlFrame* V1Frame::mutable_control_frame() { + ::nearby::presence::ControlFrame* _msg = _internal_mutable_control_frame(); + // @@protoc_insertion_point(field_mutable:nearby.presence.V1Frame.control_frame) + return _msg; +} + +// .nearby.presence.DeviceIdentityFrame device_identity_frame = 2; +inline bool V1Frame::_internal_has_device_identity_frame() const { + return Message_case() == kDeviceIdentityFrame; +} +inline bool V1Frame::has_device_identity_frame() const { + return _internal_has_device_identity_frame(); +} +inline void V1Frame::set_has_device_identity_frame() { + _oneof_case_[0] = kDeviceIdentityFrame; +} +inline void V1Frame::clear_device_identity_frame() { + if (_internal_has_device_identity_frame()) { + if (GetArenaForAllocation() == nullptr) { + delete Message_.device_identity_frame_; + } + clear_has_Message(); + } +} +inline ::nearby::presence::DeviceIdentityFrame* V1Frame::release_device_identity_frame() { + // @@protoc_insertion_point(field_release:nearby.presence.V1Frame.device_identity_frame) + if (_internal_has_device_identity_frame()) { + clear_has_Message(); + ::nearby::presence::DeviceIdentityFrame* temp = Message_.device_identity_frame_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + Message_.device_identity_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::nearby::presence::DeviceIdentityFrame& V1Frame::_internal_device_identity_frame() const { + return _internal_has_device_identity_frame() + ? *Message_.device_identity_frame_ + : reinterpret_cast< ::nearby::presence::DeviceIdentityFrame&>(::nearby::presence::_DeviceIdentityFrame_default_instance_); +} +inline const ::nearby::presence::DeviceIdentityFrame& V1Frame::device_identity_frame() const { + // @@protoc_insertion_point(field_get:nearby.presence.V1Frame.device_identity_frame) + return _internal_device_identity_frame(); +} +inline ::nearby::presence::DeviceIdentityFrame* V1Frame::unsafe_arena_release_device_identity_frame() { + // @@protoc_insertion_point(field_unsafe_arena_release:nearby.presence.V1Frame.device_identity_frame) + if (_internal_has_device_identity_frame()) { + clear_has_Message(); + ::nearby::presence::DeviceIdentityFrame* temp = Message_.device_identity_frame_; + Message_.device_identity_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void V1Frame::unsafe_arena_set_allocated_device_identity_frame(::nearby::presence::DeviceIdentityFrame* device_identity_frame) { + clear_Message(); + if (device_identity_frame) { + set_has_device_identity_frame(); + Message_.device_identity_frame_ = device_identity_frame; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:nearby.presence.V1Frame.device_identity_frame) +} +inline ::nearby::presence::DeviceIdentityFrame* V1Frame::_internal_mutable_device_identity_frame() { + if (!_internal_has_device_identity_frame()) { + clear_Message(); + set_has_device_identity_frame(); + Message_.device_identity_frame_ = CreateMaybeMessage< ::nearby::presence::DeviceIdentityFrame >(GetArenaForAllocation()); + } + return Message_.device_identity_frame_; +} +inline ::nearby::presence::DeviceIdentityFrame* V1Frame::mutable_device_identity_frame() { + ::nearby::presence::DeviceIdentityFrame* _msg = _internal_mutable_device_identity_frame(); + // @@protoc_insertion_point(field_mutable:nearby.presence.V1Frame.device_identity_frame) + return _msg; +} + +// .nearby.presence.ConnectionInitFrame connection_init_frame = 3; +inline bool V1Frame::_internal_has_connection_init_frame() const { + return Message_case() == kConnectionInitFrame; +} +inline bool V1Frame::has_connection_init_frame() const { + return _internal_has_connection_init_frame(); +} +inline void V1Frame::set_has_connection_init_frame() { + _oneof_case_[0] = kConnectionInitFrame; +} +inline void V1Frame::clear_connection_init_frame() { + if (_internal_has_connection_init_frame()) { + if (GetArenaForAllocation() == nullptr) { + delete Message_.connection_init_frame_; + } + clear_has_Message(); + } +} +inline ::nearby::presence::ConnectionInitFrame* V1Frame::release_connection_init_frame() { + // @@protoc_insertion_point(field_release:nearby.presence.V1Frame.connection_init_frame) + if (_internal_has_connection_init_frame()) { + clear_has_Message(); + ::nearby::presence::ConnectionInitFrame* temp = Message_.connection_init_frame_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + Message_.connection_init_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::nearby::presence::ConnectionInitFrame& V1Frame::_internal_connection_init_frame() const { + return _internal_has_connection_init_frame() + ? *Message_.connection_init_frame_ + : reinterpret_cast< ::nearby::presence::ConnectionInitFrame&>(::nearby::presence::_ConnectionInitFrame_default_instance_); +} +inline const ::nearby::presence::ConnectionInitFrame& V1Frame::connection_init_frame() const { + // @@protoc_insertion_point(field_get:nearby.presence.V1Frame.connection_init_frame) + return _internal_connection_init_frame(); +} +inline ::nearby::presence::ConnectionInitFrame* V1Frame::unsafe_arena_release_connection_init_frame() { + // @@protoc_insertion_point(field_unsafe_arena_release:nearby.presence.V1Frame.connection_init_frame) + if (_internal_has_connection_init_frame()) { + clear_has_Message(); + ::nearby::presence::ConnectionInitFrame* temp = Message_.connection_init_frame_; + Message_.connection_init_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void V1Frame::unsafe_arena_set_allocated_connection_init_frame(::nearby::presence::ConnectionInitFrame* connection_init_frame) { + clear_Message(); + if (connection_init_frame) { + set_has_connection_init_frame(); + Message_.connection_init_frame_ = connection_init_frame; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:nearby.presence.V1Frame.connection_init_frame) +} +inline ::nearby::presence::ConnectionInitFrame* V1Frame::_internal_mutable_connection_init_frame() { + if (!_internal_has_connection_init_frame()) { + clear_Message(); + set_has_connection_init_frame(); + Message_.connection_init_frame_ = CreateMaybeMessage< ::nearby::presence::ConnectionInitFrame >(GetArenaForAllocation()); + } + return Message_.connection_init_frame_; +} +inline ::nearby::presence::ConnectionInitFrame* V1Frame::mutable_connection_init_frame() { + ::nearby::presence::ConnectionInitFrame* _msg = _internal_mutable_connection_init_frame(); + // @@protoc_insertion_point(field_mutable:nearby.presence.V1Frame.connection_init_frame) + return _msg; +} + +// .nearby.presence.UwbControleeCapabilities uwb_controlee_capabilities_frame = 4; +inline bool V1Frame::_internal_has_uwb_controlee_capabilities_frame() const { + return Message_case() == kUwbControleeCapabilitiesFrame; +} +inline bool V1Frame::has_uwb_controlee_capabilities_frame() const { + return _internal_has_uwb_controlee_capabilities_frame(); +} +inline void V1Frame::set_has_uwb_controlee_capabilities_frame() { + _oneof_case_[0] = kUwbControleeCapabilitiesFrame; +} +inline void V1Frame::clear_uwb_controlee_capabilities_frame() { + if (_internal_has_uwb_controlee_capabilities_frame()) { + if (GetArenaForAllocation() == nullptr) { + delete Message_.uwb_controlee_capabilities_frame_; + } + clear_has_Message(); + } +} +inline ::nearby::presence::UwbControleeCapabilities* V1Frame::release_uwb_controlee_capabilities_frame() { + // @@protoc_insertion_point(field_release:nearby.presence.V1Frame.uwb_controlee_capabilities_frame) + if (_internal_has_uwb_controlee_capabilities_frame()) { + clear_has_Message(); + ::nearby::presence::UwbControleeCapabilities* temp = Message_.uwb_controlee_capabilities_frame_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + Message_.uwb_controlee_capabilities_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::nearby::presence::UwbControleeCapabilities& V1Frame::_internal_uwb_controlee_capabilities_frame() const { + return _internal_has_uwb_controlee_capabilities_frame() + ? *Message_.uwb_controlee_capabilities_frame_ + : reinterpret_cast< ::nearby::presence::UwbControleeCapabilities&>(::nearby::presence::_UwbControleeCapabilities_default_instance_); +} +inline const ::nearby::presence::UwbControleeCapabilities& V1Frame::uwb_controlee_capabilities_frame() const { + // @@protoc_insertion_point(field_get:nearby.presence.V1Frame.uwb_controlee_capabilities_frame) + return _internal_uwb_controlee_capabilities_frame(); +} +inline ::nearby::presence::UwbControleeCapabilities* V1Frame::unsafe_arena_release_uwb_controlee_capabilities_frame() { + // @@protoc_insertion_point(field_unsafe_arena_release:nearby.presence.V1Frame.uwb_controlee_capabilities_frame) + if (_internal_has_uwb_controlee_capabilities_frame()) { + clear_has_Message(); + ::nearby::presence::UwbControleeCapabilities* temp = Message_.uwb_controlee_capabilities_frame_; + Message_.uwb_controlee_capabilities_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void V1Frame::unsafe_arena_set_allocated_uwb_controlee_capabilities_frame(::nearby::presence::UwbControleeCapabilities* uwb_controlee_capabilities_frame) { + clear_Message(); + if (uwb_controlee_capabilities_frame) { + set_has_uwb_controlee_capabilities_frame(); + Message_.uwb_controlee_capabilities_frame_ = uwb_controlee_capabilities_frame; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:nearby.presence.V1Frame.uwb_controlee_capabilities_frame) +} +inline ::nearby::presence::UwbControleeCapabilities* V1Frame::_internal_mutable_uwb_controlee_capabilities_frame() { + if (!_internal_has_uwb_controlee_capabilities_frame()) { + clear_Message(); + set_has_uwb_controlee_capabilities_frame(); + Message_.uwb_controlee_capabilities_frame_ = CreateMaybeMessage< ::nearby::presence::UwbControleeCapabilities >(GetArenaForAllocation()); + } + return Message_.uwb_controlee_capabilities_frame_; +} +inline ::nearby::presence::UwbControleeCapabilities* V1Frame::mutable_uwb_controlee_capabilities_frame() { + ::nearby::presence::UwbControleeCapabilities* _msg = _internal_mutable_uwb_controlee_capabilities_frame(); + // @@protoc_insertion_point(field_mutable:nearby.presence.V1Frame.uwb_controlee_capabilities_frame) + return _msg; +} + +// .nearby.presence.UwbConnectionInfo uwb_connection_info = 5; +inline bool V1Frame::_internal_has_uwb_connection_info() const { + return Message_case() == kUwbConnectionInfo; +} +inline bool V1Frame::has_uwb_connection_info() const { + return _internal_has_uwb_connection_info(); +} +inline void V1Frame::set_has_uwb_connection_info() { + _oneof_case_[0] = kUwbConnectionInfo; +} +inline void V1Frame::clear_uwb_connection_info() { + if (_internal_has_uwb_connection_info()) { + if (GetArenaForAllocation() == nullptr) { + delete Message_.uwb_connection_info_; + } + clear_has_Message(); + } +} +inline ::nearby::presence::UwbConnectionInfo* V1Frame::release_uwb_connection_info() { + // @@protoc_insertion_point(field_release:nearby.presence.V1Frame.uwb_connection_info) + if (_internal_has_uwb_connection_info()) { + clear_has_Message(); + ::nearby::presence::UwbConnectionInfo* temp = Message_.uwb_connection_info_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + Message_.uwb_connection_info_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::nearby::presence::UwbConnectionInfo& V1Frame::_internal_uwb_connection_info() const { + return _internal_has_uwb_connection_info() + ? *Message_.uwb_connection_info_ + : reinterpret_cast< ::nearby::presence::UwbConnectionInfo&>(::nearby::presence::_UwbConnectionInfo_default_instance_); +} +inline const ::nearby::presence::UwbConnectionInfo& V1Frame::uwb_connection_info() const { + // @@protoc_insertion_point(field_get:nearby.presence.V1Frame.uwb_connection_info) + return _internal_uwb_connection_info(); +} +inline ::nearby::presence::UwbConnectionInfo* V1Frame::unsafe_arena_release_uwb_connection_info() { + // @@protoc_insertion_point(field_unsafe_arena_release:nearby.presence.V1Frame.uwb_connection_info) + if (_internal_has_uwb_connection_info()) { + clear_has_Message(); + ::nearby::presence::UwbConnectionInfo* temp = Message_.uwb_connection_info_; + Message_.uwb_connection_info_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void V1Frame::unsafe_arena_set_allocated_uwb_connection_info(::nearby::presence::UwbConnectionInfo* uwb_connection_info) { + clear_Message(); + if (uwb_connection_info) { + set_has_uwb_connection_info(); + Message_.uwb_connection_info_ = uwb_connection_info; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:nearby.presence.V1Frame.uwb_connection_info) +} +inline ::nearby::presence::UwbConnectionInfo* V1Frame::_internal_mutable_uwb_connection_info() { + if (!_internal_has_uwb_connection_info()) { + clear_Message(); + set_has_uwb_connection_info(); + Message_.uwb_connection_info_ = CreateMaybeMessage< ::nearby::presence::UwbConnectionInfo >(GetArenaForAllocation()); + } + return Message_.uwb_connection_info_; +} +inline ::nearby::presence::UwbConnectionInfo* V1Frame::mutable_uwb_connection_info() { + ::nearby::presence::UwbConnectionInfo* _msg = _internal_mutable_uwb_connection_info(); + // @@protoc_insertion_point(field_mutable:nearby.presence.V1Frame.uwb_connection_info) + return _msg; +} + +// .nearby.presence.PresenceAuthenticationFrame authentication_frame = 6; +inline bool V1Frame::_internal_has_authentication_frame() const { + return Message_case() == kAuthenticationFrame; +} +inline bool V1Frame::has_authentication_frame() const { + return _internal_has_authentication_frame(); +} +inline void V1Frame::set_has_authentication_frame() { + _oneof_case_[0] = kAuthenticationFrame; +} +inline void V1Frame::clear_authentication_frame() { + if (_internal_has_authentication_frame()) { + if (GetArenaForAllocation() == nullptr) { + delete Message_.authentication_frame_; + } + clear_has_Message(); + } +} +inline ::nearby::presence::PresenceAuthenticationFrame* V1Frame::release_authentication_frame() { + // @@protoc_insertion_point(field_release:nearby.presence.V1Frame.authentication_frame) + if (_internal_has_authentication_frame()) { + clear_has_Message(); + ::nearby::presence::PresenceAuthenticationFrame* temp = Message_.authentication_frame_; + if (GetArenaForAllocation() != nullptr) { + temp = ::PROTOBUF_NAMESPACE_ID::internal::DuplicateIfNonNull(temp); + } + Message_.authentication_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline const ::nearby::presence::PresenceAuthenticationFrame& V1Frame::_internal_authentication_frame() const { + return _internal_has_authentication_frame() + ? *Message_.authentication_frame_ + : reinterpret_cast< ::nearby::presence::PresenceAuthenticationFrame&>(::nearby::presence::_PresenceAuthenticationFrame_default_instance_); +} +inline const ::nearby::presence::PresenceAuthenticationFrame& V1Frame::authentication_frame() const { + // @@protoc_insertion_point(field_get:nearby.presence.V1Frame.authentication_frame) + return _internal_authentication_frame(); +} +inline ::nearby::presence::PresenceAuthenticationFrame* V1Frame::unsafe_arena_release_authentication_frame() { + // @@protoc_insertion_point(field_unsafe_arena_release:nearby.presence.V1Frame.authentication_frame) + if (_internal_has_authentication_frame()) { + clear_has_Message(); + ::nearby::presence::PresenceAuthenticationFrame* temp = Message_.authentication_frame_; + Message_.authentication_frame_ = nullptr; + return temp; + } else { + return nullptr; + } +} +inline void V1Frame::unsafe_arena_set_allocated_authentication_frame(::nearby::presence::PresenceAuthenticationFrame* authentication_frame) { + clear_Message(); + if (authentication_frame) { + set_has_authentication_frame(); + Message_.authentication_frame_ = authentication_frame; + } + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:nearby.presence.V1Frame.authentication_frame) +} +inline ::nearby::presence::PresenceAuthenticationFrame* V1Frame::_internal_mutable_authentication_frame() { + if (!_internal_has_authentication_frame()) { + clear_Message(); + set_has_authentication_frame(); + Message_.authentication_frame_ = CreateMaybeMessage< ::nearby::presence::PresenceAuthenticationFrame >(GetArenaForAllocation()); + } + return Message_.authentication_frame_; +} +inline ::nearby::presence::PresenceAuthenticationFrame* V1Frame::mutable_authentication_frame() { + ::nearby::presence::PresenceAuthenticationFrame* _msg = _internal_mutable_authentication_frame(); + // @@protoc_insertion_point(field_mutable:nearby.presence.V1Frame.authentication_frame) + return _msg; +} + +inline bool V1Frame::has_Message() const { + return Message_case() != MESSAGE_NOT_SET; +} +inline void V1Frame::clear_has_Message() { + _oneof_case_[0] = MESSAGE_NOT_SET; +} +inline V1Frame::MessageCase V1Frame::Message_case() const { + return V1Frame::MessageCase(_oneof_case_[0]); +} +// ------------------------------------------------------------------- + +// DeviceIdentityFrame + +// optional string device_name = 1; +inline bool DeviceIdentityFrame::_internal_has_device_name() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + return value; +} +inline bool DeviceIdentityFrame::has_device_name() const { + return _internal_has_device_name(); +} +inline void DeviceIdentityFrame::clear_device_name() { + device_name_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000001u; +} +inline const std::string& DeviceIdentityFrame::device_name() const { + // @@protoc_insertion_point(field_get:nearby.presence.DeviceIdentityFrame.device_name) + return _internal_device_name(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void DeviceIdentityFrame::set_device_name(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000001u; + device_name_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.DeviceIdentityFrame.device_name) +} +inline std::string* DeviceIdentityFrame::mutable_device_name() { + std::string* _s = _internal_mutable_device_name(); + // @@protoc_insertion_point(field_mutable:nearby.presence.DeviceIdentityFrame.device_name) + return _s; +} +inline const std::string& DeviceIdentityFrame::_internal_device_name() const { + return device_name_.Get(); +} +inline void DeviceIdentityFrame::_internal_set_device_name(const std::string& value) { + _has_bits_[0] |= 0x00000001u; + device_name_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::_internal_mutable_device_name() { + _has_bits_[0] |= 0x00000001u; + return device_name_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::release_device_name() { + // @@protoc_insertion_point(field_release:nearby.presence.DeviceIdentityFrame.device_name) + if (!_internal_has_device_name()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000001u; + auto* p = device_name_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (device_name_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + device_name_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void DeviceIdentityFrame::set_allocated_device_name(std::string* device_name) { + if (device_name != nullptr) { + _has_bits_[0] |= 0x00000001u; + } else { + _has_bits_[0] &= ~0x00000001u; + } + device_name_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), device_name, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (device_name_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + device_name_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.DeviceIdentityFrame.device_name) +} + +// optional bytes bluetooth_mac_address = 2; +inline bool DeviceIdentityFrame::_internal_has_bluetooth_mac_address() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool DeviceIdentityFrame::has_bluetooth_mac_address() const { + return _internal_has_bluetooth_mac_address(); +} +inline void DeviceIdentityFrame::clear_bluetooth_mac_address() { + bluetooth_mac_address_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000002u; +} +inline const std::string& DeviceIdentityFrame::bluetooth_mac_address() const { + // @@protoc_insertion_point(field_get:nearby.presence.DeviceIdentityFrame.bluetooth_mac_address) + return _internal_bluetooth_mac_address(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void DeviceIdentityFrame::set_bluetooth_mac_address(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000002u; + bluetooth_mac_address_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.DeviceIdentityFrame.bluetooth_mac_address) +} +inline std::string* DeviceIdentityFrame::mutable_bluetooth_mac_address() { + std::string* _s = _internal_mutable_bluetooth_mac_address(); + // @@protoc_insertion_point(field_mutable:nearby.presence.DeviceIdentityFrame.bluetooth_mac_address) + return _s; +} +inline const std::string& DeviceIdentityFrame::_internal_bluetooth_mac_address() const { + return bluetooth_mac_address_.Get(); +} +inline void DeviceIdentityFrame::_internal_set_bluetooth_mac_address(const std::string& value) { + _has_bits_[0] |= 0x00000002u; + bluetooth_mac_address_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::_internal_mutable_bluetooth_mac_address() { + _has_bits_[0] |= 0x00000002u; + return bluetooth_mac_address_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::release_bluetooth_mac_address() { + // @@protoc_insertion_point(field_release:nearby.presence.DeviceIdentityFrame.bluetooth_mac_address) + if (!_internal_has_bluetooth_mac_address()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000002u; + auto* p = bluetooth_mac_address_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (bluetooth_mac_address_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + bluetooth_mac_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void DeviceIdentityFrame::set_allocated_bluetooth_mac_address(std::string* bluetooth_mac_address) { + if (bluetooth_mac_address != nullptr) { + _has_bits_[0] |= 0x00000002u; + } else { + _has_bits_[0] &= ~0x00000002u; + } + bluetooth_mac_address_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), bluetooth_mac_address, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (bluetooth_mac_address_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + bluetooth_mac_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.DeviceIdentityFrame.bluetooth_mac_address) +} + +// optional string device_image_url = 3; +inline bool DeviceIdentityFrame::_internal_has_device_image_url() const { + bool value = (_has_bits_[0] & 0x00000004u) != 0; + return value; +} +inline bool DeviceIdentityFrame::has_device_image_url() const { + return _internal_has_device_image_url(); +} +inline void DeviceIdentityFrame::clear_device_image_url() { + device_image_url_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000004u; +} +inline const std::string& DeviceIdentityFrame::device_image_url() const { + // @@protoc_insertion_point(field_get:nearby.presence.DeviceIdentityFrame.device_image_url) + return _internal_device_image_url(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void DeviceIdentityFrame::set_device_image_url(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000004u; + device_image_url_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.DeviceIdentityFrame.device_image_url) +} +inline std::string* DeviceIdentityFrame::mutable_device_image_url() { + std::string* _s = _internal_mutable_device_image_url(); + // @@protoc_insertion_point(field_mutable:nearby.presence.DeviceIdentityFrame.device_image_url) + return _s; +} +inline const std::string& DeviceIdentityFrame::_internal_device_image_url() const { + return device_image_url_.Get(); +} +inline void DeviceIdentityFrame::_internal_set_device_image_url(const std::string& value) { + _has_bits_[0] |= 0x00000004u; + device_image_url_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::_internal_mutable_device_image_url() { + _has_bits_[0] |= 0x00000004u; + return device_image_url_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::release_device_image_url() { + // @@protoc_insertion_point(field_release:nearby.presence.DeviceIdentityFrame.device_image_url) + if (!_internal_has_device_image_url()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000004u; + auto* p = device_image_url_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (device_image_url_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + device_image_url_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void DeviceIdentityFrame::set_allocated_device_image_url(std::string* device_image_url) { + if (device_image_url != nullptr) { + _has_bits_[0] |= 0x00000004u; + } else { + _has_bits_[0] &= ~0x00000004u; + } + device_image_url_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), device_image_url, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (device_image_url_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + device_image_url_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.DeviceIdentityFrame.device_image_url) +} + +// optional string model_id = 4; +inline bool DeviceIdentityFrame::_internal_has_model_id() const { + bool value = (_has_bits_[0] & 0x00000008u) != 0; + return value; +} +inline bool DeviceIdentityFrame::has_model_id() const { + return _internal_has_model_id(); +} +inline void DeviceIdentityFrame::clear_model_id() { + model_id_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000008u; +} +inline const std::string& DeviceIdentityFrame::model_id() const { + // @@protoc_insertion_point(field_get:nearby.presence.DeviceIdentityFrame.model_id) + return _internal_model_id(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void DeviceIdentityFrame::set_model_id(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000008u; + model_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.DeviceIdentityFrame.model_id) +} +inline std::string* DeviceIdentityFrame::mutable_model_id() { + std::string* _s = _internal_mutable_model_id(); + // @@protoc_insertion_point(field_mutable:nearby.presence.DeviceIdentityFrame.model_id) + return _s; +} +inline const std::string& DeviceIdentityFrame::_internal_model_id() const { + return model_id_.Get(); +} +inline void DeviceIdentityFrame::_internal_set_model_id(const std::string& value) { + _has_bits_[0] |= 0x00000008u; + model_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::_internal_mutable_model_id() { + _has_bits_[0] |= 0x00000008u; + return model_id_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::release_model_id() { + // @@protoc_insertion_point(field_release:nearby.presence.DeviceIdentityFrame.model_id) + if (!_internal_has_model_id()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000008u; + auto* p = model_id_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (model_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + model_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void DeviceIdentityFrame::set_allocated_model_id(std::string* model_id) { + if (model_id != nullptr) { + _has_bits_[0] |= 0x00000008u; + } else { + _has_bits_[0] &= ~0x00000008u; + } + model_id_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), model_id, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (model_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + model_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.DeviceIdentityFrame.model_id) +} + +// repeated int32 action = 5 [packed = true]; +inline int DeviceIdentityFrame::_internal_action_size() const { + return action_.size(); +} +inline int DeviceIdentityFrame::action_size() const { + return _internal_action_size(); +} +inline void DeviceIdentityFrame::clear_action() { + action_.Clear(); +} +inline int32_t DeviceIdentityFrame::_internal_action(int index) const { + return action_.Get(index); +} +inline int32_t DeviceIdentityFrame::action(int index) const { + // @@protoc_insertion_point(field_get:nearby.presence.DeviceIdentityFrame.action) + return _internal_action(index); +} +inline void DeviceIdentityFrame::set_action(int index, int32_t value) { + action_.Set(index, value); + // @@protoc_insertion_point(field_set:nearby.presence.DeviceIdentityFrame.action) +} +inline void DeviceIdentityFrame::_internal_add_action(int32_t value) { + action_.Add(value); +} +inline void DeviceIdentityFrame::add_action(int32_t value) { + _internal_add_action(value); + // @@protoc_insertion_point(field_add:nearby.presence.DeviceIdentityFrame.action) +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +DeviceIdentityFrame::_internal_action() const { + return action_; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +DeviceIdentityFrame::action() const { + // @@protoc_insertion_point(field_list:nearby.presence.DeviceIdentityFrame.action) + return _internal_action(); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +DeviceIdentityFrame::_internal_mutable_action() { + return &action_; +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +DeviceIdentityFrame::mutable_action() { + // @@protoc_insertion_point(field_mutable_list:nearby.presence.DeviceIdentityFrame.action) + return _internal_mutable_action(); +} + +// optional string device_model_name = 6; +inline bool DeviceIdentityFrame::_internal_has_device_model_name() const { + bool value = (_has_bits_[0] & 0x00000010u) != 0; + return value; +} +inline bool DeviceIdentityFrame::has_device_model_name() const { + return _internal_has_device_model_name(); +} +inline void DeviceIdentityFrame::clear_device_model_name() { + device_model_name_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000010u; +} +inline const std::string& DeviceIdentityFrame::device_model_name() const { + // @@protoc_insertion_point(field_get:nearby.presence.DeviceIdentityFrame.device_model_name) + return _internal_device_model_name(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void DeviceIdentityFrame::set_device_model_name(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000010u; + device_model_name_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.DeviceIdentityFrame.device_model_name) +} +inline std::string* DeviceIdentityFrame::mutable_device_model_name() { + std::string* _s = _internal_mutable_device_model_name(); + // @@protoc_insertion_point(field_mutable:nearby.presence.DeviceIdentityFrame.device_model_name) + return _s; +} +inline const std::string& DeviceIdentityFrame::_internal_device_model_name() const { + return device_model_name_.Get(); +} +inline void DeviceIdentityFrame::_internal_set_device_model_name(const std::string& value) { + _has_bits_[0] |= 0x00000010u; + device_model_name_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::_internal_mutable_device_model_name() { + _has_bits_[0] |= 0x00000010u; + return device_model_name_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* DeviceIdentityFrame::release_device_model_name() { + // @@protoc_insertion_point(field_release:nearby.presence.DeviceIdentityFrame.device_model_name) + if (!_internal_has_device_model_name()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000010u; + auto* p = device_model_name_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (device_model_name_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + device_model_name_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void DeviceIdentityFrame::set_allocated_device_model_name(std::string* device_model_name) { + if (device_model_name != nullptr) { + _has_bits_[0] |= 0x00000010u; + } else { + _has_bits_[0] &= ~0x00000010u; + } + device_model_name_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), device_model_name, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (device_model_name_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + device_model_name_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.DeviceIdentityFrame.device_model_name) +} + +// optional int32 device_type = 7; +inline bool DeviceIdentityFrame::_internal_has_device_type() const { + bool value = (_has_bits_[0] & 0x00000020u) != 0; + return value; +} +inline bool DeviceIdentityFrame::has_device_type() const { + return _internal_has_device_type(); +} +inline void DeviceIdentityFrame::clear_device_type() { + device_type_ = 0; + _has_bits_[0] &= ~0x00000020u; +} +inline int32_t DeviceIdentityFrame::_internal_device_type() const { + return device_type_; +} +inline int32_t DeviceIdentityFrame::device_type() const { + // @@protoc_insertion_point(field_get:nearby.presence.DeviceIdentityFrame.device_type) + return _internal_device_type(); +} +inline void DeviceIdentityFrame::_internal_set_device_type(int32_t value) { + _has_bits_[0] |= 0x00000020u; + device_type_ = value; +} +inline void DeviceIdentityFrame::set_device_type(int32_t value) { + _internal_set_device_type(value); + // @@protoc_insertion_point(field_set:nearby.presence.DeviceIdentityFrame.device_type) +} + +// ------------------------------------------------------------------- + +// ConnectionInitFrame + +// repeated int32 actions = 1 [packed = true]; +inline int ConnectionInitFrame::_internal_actions_size() const { + return actions_.size(); +} +inline int ConnectionInitFrame::actions_size() const { + return _internal_actions_size(); +} +inline void ConnectionInitFrame::clear_actions() { + actions_.Clear(); +} +inline int32_t ConnectionInitFrame::_internal_actions(int index) const { + return actions_.Get(index); +} +inline int32_t ConnectionInitFrame::actions(int index) const { + // @@protoc_insertion_point(field_get:nearby.presence.ConnectionInitFrame.actions) + return _internal_actions(index); +} +inline void ConnectionInitFrame::set_actions(int index, int32_t value) { + actions_.Set(index, value); + // @@protoc_insertion_point(field_set:nearby.presence.ConnectionInitFrame.actions) +} +inline void ConnectionInitFrame::_internal_add_actions(int32_t value) { + actions_.Add(value); +} +inline void ConnectionInitFrame::add_actions(int32_t value) { + _internal_add_actions(value); + // @@protoc_insertion_point(field_add:nearby.presence.ConnectionInitFrame.actions) +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +ConnectionInitFrame::_internal_actions() const { + return actions_; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +ConnectionInitFrame::actions() const { + // @@protoc_insertion_point(field_list:nearby.presence.ConnectionInitFrame.actions) + return _internal_actions(); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +ConnectionInitFrame::_internal_mutable_actions() { + return &actions_; +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +ConnectionInitFrame::mutable_actions() { + // @@protoc_insertion_point(field_mutable_list:nearby.presence.ConnectionInitFrame.actions) + return _internal_mutable_actions(); +} + +// optional int32 identity_type = 2; +inline bool ConnectionInitFrame::_internal_has_identity_type() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + return value; +} +inline bool ConnectionInitFrame::has_identity_type() const { + return _internal_has_identity_type(); +} +inline void ConnectionInitFrame::clear_identity_type() { + identity_type_ = 0; + _has_bits_[0] &= ~0x00000001u; +} +inline int32_t ConnectionInitFrame::_internal_identity_type() const { + return identity_type_; +} +inline int32_t ConnectionInitFrame::identity_type() const { + // @@protoc_insertion_point(field_get:nearby.presence.ConnectionInitFrame.identity_type) + return _internal_identity_type(); +} +inline void ConnectionInitFrame::_internal_set_identity_type(int32_t value) { + _has_bits_[0] |= 0x00000001u; + identity_type_ = value; +} +inline void ConnectionInitFrame::set_identity_type(int32_t value) { + _internal_set_identity_type(value); + // @@protoc_insertion_point(field_set:nearby.presence.ConnectionInitFrame.identity_type) +} + +// optional bool uwb_enable = 3; +inline bool ConnectionInitFrame::_internal_has_uwb_enable() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool ConnectionInitFrame::has_uwb_enable() const { + return _internal_has_uwb_enable(); +} +inline void ConnectionInitFrame::clear_uwb_enable() { + uwb_enable_ = false; + _has_bits_[0] &= ~0x00000002u; +} +inline bool ConnectionInitFrame::_internal_uwb_enable() const { + return uwb_enable_; +} +inline bool ConnectionInitFrame::uwb_enable() const { + // @@protoc_insertion_point(field_get:nearby.presence.ConnectionInitFrame.uwb_enable) + return _internal_uwb_enable(); +} +inline void ConnectionInitFrame::_internal_set_uwb_enable(bool value) { + _has_bits_[0] |= 0x00000002u; + uwb_enable_ = value; +} +inline void ConnectionInitFrame::set_uwb_enable(bool value) { + _internal_set_uwb_enable(value); + // @@protoc_insertion_point(field_set:nearby.presence.ConnectionInitFrame.uwb_enable) +} + +// optional int64 device_unique_id = 4; +inline bool ConnectionInitFrame::_internal_has_device_unique_id() const { + bool value = (_has_bits_[0] & 0x00000004u) != 0; + return value; +} +inline bool ConnectionInitFrame::has_device_unique_id() const { + return _internal_has_device_unique_id(); +} +inline void ConnectionInitFrame::clear_device_unique_id() { + device_unique_id_ = int64_t{0}; + _has_bits_[0] &= ~0x00000004u; +} +inline int64_t ConnectionInitFrame::_internal_device_unique_id() const { + return device_unique_id_; +} +inline int64_t ConnectionInitFrame::device_unique_id() const { + // @@protoc_insertion_point(field_get:nearby.presence.ConnectionInitFrame.device_unique_id) + return _internal_device_unique_id(); +} +inline void ConnectionInitFrame::_internal_set_device_unique_id(int64_t value) { + _has_bits_[0] |= 0x00000004u; + device_unique_id_ = value; +} +inline void ConnectionInitFrame::set_device_unique_id(int64_t value) { + _internal_set_device_unique_id(value); + // @@protoc_insertion_point(field_set:nearby.presence.ConnectionInitFrame.device_unique_id) +} + +// ------------------------------------------------------------------- + +// UwbControleeCapabilities + +// optional bytes controlee_address = 1; +inline bool UwbControleeCapabilities::_internal_has_controlee_address() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_controlee_address() const { + return _internal_has_controlee_address(); +} +inline void UwbControleeCapabilities::clear_controlee_address() { + controlee_address_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000001u; +} +inline const std::string& UwbControleeCapabilities::controlee_address() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.controlee_address) + return _internal_controlee_address(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void UwbControleeCapabilities::set_controlee_address(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000001u; + controlee_address_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.controlee_address) +} +inline std::string* UwbControleeCapabilities::mutable_controlee_address() { + std::string* _s = _internal_mutable_controlee_address(); + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbControleeCapabilities.controlee_address) + return _s; +} +inline const std::string& UwbControleeCapabilities::_internal_controlee_address() const { + return controlee_address_.Get(); +} +inline void UwbControleeCapabilities::_internal_set_controlee_address(const std::string& value) { + _has_bits_[0] |= 0x00000001u; + controlee_address_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* UwbControleeCapabilities::_internal_mutable_controlee_address() { + _has_bits_[0] |= 0x00000001u; + return controlee_address_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* UwbControleeCapabilities::release_controlee_address() { + // @@protoc_insertion_point(field_release:nearby.presence.UwbControleeCapabilities.controlee_address) + if (!_internal_has_controlee_address()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000001u; + auto* p = controlee_address_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (controlee_address_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + controlee_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void UwbControleeCapabilities::set_allocated_controlee_address(std::string* controlee_address) { + if (controlee_address != nullptr) { + _has_bits_[0] |= 0x00000001u; + } else { + _has_bits_[0] &= ~0x00000001u; + } + controlee_address_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), controlee_address, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (controlee_address_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + controlee_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.UwbControleeCapabilities.controlee_address) +} + +// repeated int32 supported_config_ids = 2 [packed = true]; +inline int UwbControleeCapabilities::_internal_supported_config_ids_size() const { + return supported_config_ids_.size(); +} +inline int UwbControleeCapabilities::supported_config_ids_size() const { + return _internal_supported_config_ids_size(); +} +inline void UwbControleeCapabilities::clear_supported_config_ids() { + supported_config_ids_.Clear(); +} +inline int32_t UwbControleeCapabilities::_internal_supported_config_ids(int index) const { + return supported_config_ids_.Get(index); +} +inline int32_t UwbControleeCapabilities::supported_config_ids(int index) const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.supported_config_ids) + return _internal_supported_config_ids(index); +} +inline void UwbControleeCapabilities::set_supported_config_ids(int index, int32_t value) { + supported_config_ids_.Set(index, value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.supported_config_ids) +} +inline void UwbControleeCapabilities::_internal_add_supported_config_ids(int32_t value) { + supported_config_ids_.Add(value); +} +inline void UwbControleeCapabilities::add_supported_config_ids(int32_t value) { + _internal_add_supported_config_ids(value); + // @@protoc_insertion_point(field_add:nearby.presence.UwbControleeCapabilities.supported_config_ids) +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::_internal_supported_config_ids() const { + return supported_config_ids_; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::supported_config_ids() const { + // @@protoc_insertion_point(field_list:nearby.presence.UwbControleeCapabilities.supported_config_ids) + return _internal_supported_config_ids(); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::_internal_mutable_supported_config_ids() { + return &supported_config_ids_; +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::mutable_supported_config_ids() { + // @@protoc_insertion_point(field_mutable_list:nearby.presence.UwbControleeCapabilities.supported_config_ids) + return _internal_mutable_supported_config_ids(); +} + +// repeated int32 supported_channels = 3 [packed = true]; +inline int UwbControleeCapabilities::_internal_supported_channels_size() const { + return supported_channels_.size(); +} +inline int UwbControleeCapabilities::supported_channels_size() const { + return _internal_supported_channels_size(); +} +inline void UwbControleeCapabilities::clear_supported_channels() { + supported_channels_.Clear(); +} +inline int32_t UwbControleeCapabilities::_internal_supported_channels(int index) const { + return supported_channels_.Get(index); +} +inline int32_t UwbControleeCapabilities::supported_channels(int index) const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.supported_channels) + return _internal_supported_channels(index); +} +inline void UwbControleeCapabilities::set_supported_channels(int index, int32_t value) { + supported_channels_.Set(index, value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.supported_channels) +} +inline void UwbControleeCapabilities::_internal_add_supported_channels(int32_t value) { + supported_channels_.Add(value); +} +inline void UwbControleeCapabilities::add_supported_channels(int32_t value) { + _internal_add_supported_channels(value); + // @@protoc_insertion_point(field_add:nearby.presence.UwbControleeCapabilities.supported_channels) +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::_internal_supported_channels() const { + return supported_channels_; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::supported_channels() const { + // @@protoc_insertion_point(field_list:nearby.presence.UwbControleeCapabilities.supported_channels) + return _internal_supported_channels(); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::_internal_mutable_supported_channels() { + return &supported_channels_; +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::mutable_supported_channels() { + // @@protoc_insertion_point(field_mutable_list:nearby.presence.UwbControleeCapabilities.supported_channels) + return _internal_mutable_supported_channels(); +} + +// optional int32 min_ranging_interval_ms = 4; +inline bool UwbControleeCapabilities::_internal_has_min_ranging_interval_ms() const { + bool value = (_has_bits_[0] & 0x00000010u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_min_ranging_interval_ms() const { + return _internal_has_min_ranging_interval_ms(); +} +inline void UwbControleeCapabilities::clear_min_ranging_interval_ms() { + min_ranging_interval_ms_ = 0; + _has_bits_[0] &= ~0x00000010u; +} +inline int32_t UwbControleeCapabilities::_internal_min_ranging_interval_ms() const { + return min_ranging_interval_ms_; +} +inline int32_t UwbControleeCapabilities::min_ranging_interval_ms() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.min_ranging_interval_ms) + return _internal_min_ranging_interval_ms(); +} +inline void UwbControleeCapabilities::_internal_set_min_ranging_interval_ms(int32_t value) { + _has_bits_[0] |= 0x00000010u; + min_ranging_interval_ms_ = value; +} +inline void UwbControleeCapabilities::set_min_ranging_interval_ms(int32_t value) { + _internal_set_min_ranging_interval_ms(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.min_ranging_interval_ms) +} + +// optional bytes sub_session_id = 5; +inline bool UwbControleeCapabilities::_internal_has_sub_session_id() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_sub_session_id() const { + return _internal_has_sub_session_id(); +} +inline void UwbControleeCapabilities::clear_sub_session_id() { + sub_session_id_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000002u; +} +inline const std::string& UwbControleeCapabilities::sub_session_id() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.sub_session_id) + return _internal_sub_session_id(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void UwbControleeCapabilities::set_sub_session_id(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000002u; + sub_session_id_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.sub_session_id) +} +inline std::string* UwbControleeCapabilities::mutable_sub_session_id() { + std::string* _s = _internal_mutable_sub_session_id(); + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbControleeCapabilities.sub_session_id) + return _s; +} +inline const std::string& UwbControleeCapabilities::_internal_sub_session_id() const { + return sub_session_id_.Get(); +} +inline void UwbControleeCapabilities::_internal_set_sub_session_id(const std::string& value) { + _has_bits_[0] |= 0x00000002u; + sub_session_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* UwbControleeCapabilities::_internal_mutable_sub_session_id() { + _has_bits_[0] |= 0x00000002u; + return sub_session_id_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* UwbControleeCapabilities::release_sub_session_id() { + // @@protoc_insertion_point(field_release:nearby.presence.UwbControleeCapabilities.sub_session_id) + if (!_internal_has_sub_session_id()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000002u; + auto* p = sub_session_id_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (sub_session_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + sub_session_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void UwbControleeCapabilities::set_allocated_sub_session_id(std::string* sub_session_id) { + if (sub_session_id != nullptr) { + _has_bits_[0] |= 0x00000002u; + } else { + _has_bits_[0] &= ~0x00000002u; + } + sub_session_id_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), sub_session_id, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (sub_session_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + sub_session_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.UwbControleeCapabilities.sub_session_id) +} + +// optional bytes sub_session_key = 6; +inline bool UwbControleeCapabilities::_internal_has_sub_session_key() const { + bool value = (_has_bits_[0] & 0x00000004u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_sub_session_key() const { + return _internal_has_sub_session_key(); +} +inline void UwbControleeCapabilities::clear_sub_session_key() { + sub_session_key_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000004u; +} +inline const std::string& UwbControleeCapabilities::sub_session_key() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.sub_session_key) + return _internal_sub_session_key(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void UwbControleeCapabilities::set_sub_session_key(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000004u; + sub_session_key_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.sub_session_key) +} +inline std::string* UwbControleeCapabilities::mutable_sub_session_key() { + std::string* _s = _internal_mutable_sub_session_key(); + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbControleeCapabilities.sub_session_key) + return _s; +} +inline const std::string& UwbControleeCapabilities::_internal_sub_session_key() const { + return sub_session_key_.Get(); +} +inline void UwbControleeCapabilities::_internal_set_sub_session_key(const std::string& value) { + _has_bits_[0] |= 0x00000004u; + sub_session_key_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* UwbControleeCapabilities::_internal_mutable_sub_session_key() { + _has_bits_[0] |= 0x00000004u; + return sub_session_key_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* UwbControleeCapabilities::release_sub_session_key() { + // @@protoc_insertion_point(field_release:nearby.presence.UwbControleeCapabilities.sub_session_key) + if (!_internal_has_sub_session_key()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000004u; + auto* p = sub_session_key_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (sub_session_key_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + sub_session_key_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void UwbControleeCapabilities::set_allocated_sub_session_key(std::string* sub_session_key) { + if (sub_session_key != nullptr) { + _has_bits_[0] |= 0x00000004u; + } else { + _has_bits_[0] &= ~0x00000004u; + } + sub_session_key_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), sub_session_key, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (sub_session_key_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + sub_session_key_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.UwbControleeCapabilities.sub_session_key) +} + +// optional bool ranging_disabled = 7; +inline bool UwbControleeCapabilities::_internal_has_ranging_disabled() const { + bool value = (_has_bits_[0] & 0x00000020u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_ranging_disabled() const { + return _internal_has_ranging_disabled(); +} +inline void UwbControleeCapabilities::clear_ranging_disabled() { + ranging_disabled_ = false; + _has_bits_[0] &= ~0x00000020u; +} +inline bool UwbControleeCapabilities::_internal_ranging_disabled() const { + return ranging_disabled_; +} +inline bool UwbControleeCapabilities::ranging_disabled() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.ranging_disabled) + return _internal_ranging_disabled(); +} +inline void UwbControleeCapabilities::_internal_set_ranging_disabled(bool value) { + _has_bits_[0] |= 0x00000020u; + ranging_disabled_ = value; +} +inline void UwbControleeCapabilities::set_ranging_disabled(bool value) { + _internal_set_ranging_disabled(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.ranging_disabled) +} + +// optional int64 device_unique_id = 8; +inline bool UwbControleeCapabilities::_internal_has_device_unique_id() const { + bool value = (_has_bits_[0] & 0x00000008u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_device_unique_id() const { + return _internal_has_device_unique_id(); +} +inline void UwbControleeCapabilities::clear_device_unique_id() { + device_unique_id_ = int64_t{0}; + _has_bits_[0] &= ~0x00000008u; +} +inline int64_t UwbControleeCapabilities::_internal_device_unique_id() const { + return device_unique_id_; +} +inline int64_t UwbControleeCapabilities::device_unique_id() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.device_unique_id) + return _internal_device_unique_id(); +} +inline void UwbControleeCapabilities::_internal_set_device_unique_id(int64_t value) { + _has_bits_[0] |= 0x00000008u; + device_unique_id_ = value; +} +inline void UwbControleeCapabilities::set_device_unique_id(int64_t value) { + _internal_set_device_unique_id(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.device_unique_id) +} + +// optional bool is_distance_supported = 9 [default = true]; +inline bool UwbControleeCapabilities::_internal_has_is_distance_supported() const { + bool value = (_has_bits_[0] & 0x00000200u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_is_distance_supported() const { + return _internal_has_is_distance_supported(); +} +inline void UwbControleeCapabilities::clear_is_distance_supported() { + is_distance_supported_ = true; + _has_bits_[0] &= ~0x00000200u; +} +inline bool UwbControleeCapabilities::_internal_is_distance_supported() const { + return is_distance_supported_; +} +inline bool UwbControleeCapabilities::is_distance_supported() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.is_distance_supported) + return _internal_is_distance_supported(); +} +inline void UwbControleeCapabilities::_internal_set_is_distance_supported(bool value) { + _has_bits_[0] |= 0x00000200u; + is_distance_supported_ = value; +} +inline void UwbControleeCapabilities::set_is_distance_supported(bool value) { + _internal_set_is_distance_supported(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.is_distance_supported) +} + +// optional bool is_azimuth_supported = 10 [default = true]; +inline bool UwbControleeCapabilities::_internal_has_is_azimuth_supported() const { + bool value = (_has_bits_[0] & 0x00000400u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_is_azimuth_supported() const { + return _internal_has_is_azimuth_supported(); +} +inline void UwbControleeCapabilities::clear_is_azimuth_supported() { + is_azimuth_supported_ = true; + _has_bits_[0] &= ~0x00000400u; +} +inline bool UwbControleeCapabilities::_internal_is_azimuth_supported() const { + return is_azimuth_supported_; +} +inline bool UwbControleeCapabilities::is_azimuth_supported() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.is_azimuth_supported) + return _internal_is_azimuth_supported(); +} +inline void UwbControleeCapabilities::_internal_set_is_azimuth_supported(bool value) { + _has_bits_[0] |= 0x00000400u; + is_azimuth_supported_ = value; +} +inline void UwbControleeCapabilities::set_is_azimuth_supported(bool value) { + _internal_set_is_azimuth_supported(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.is_azimuth_supported) +} + +// optional bool is_elevation_supported = 11 [default = false]; +inline bool UwbControleeCapabilities::_internal_has_is_elevation_supported() const { + bool value = (_has_bits_[0] & 0x00000040u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_is_elevation_supported() const { + return _internal_has_is_elevation_supported(); +} +inline void UwbControleeCapabilities::clear_is_elevation_supported() { + is_elevation_supported_ = false; + _has_bits_[0] &= ~0x00000040u; +} +inline bool UwbControleeCapabilities::_internal_is_elevation_supported() const { + return is_elevation_supported_; +} +inline bool UwbControleeCapabilities::is_elevation_supported() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.is_elevation_supported) + return _internal_is_elevation_supported(); +} +inline void UwbControleeCapabilities::_internal_set_is_elevation_supported(bool value) { + _has_bits_[0] |= 0x00000040u; + is_elevation_supported_ = value; +} +inline void UwbControleeCapabilities::set_is_elevation_supported(bool value) { + _internal_set_is_elevation_supported(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.is_elevation_supported) +} + +// optional float min_slot_duration_ms = 12 [default = 2]; +inline bool UwbControleeCapabilities::_internal_has_min_slot_duration_ms() const { + bool value = (_has_bits_[0] & 0x00000800u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_min_slot_duration_ms() const { + return _internal_has_min_slot_duration_ms(); +} +inline void UwbControleeCapabilities::clear_min_slot_duration_ms() { + min_slot_duration_ms_ = 2; + _has_bits_[0] &= ~0x00000800u; +} +inline float UwbControleeCapabilities::_internal_min_slot_duration_ms() const { + return min_slot_duration_ms_; +} +inline float UwbControleeCapabilities::min_slot_duration_ms() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.min_slot_duration_ms) + return _internal_min_slot_duration_ms(); +} +inline void UwbControleeCapabilities::_internal_set_min_slot_duration_ms(float value) { + _has_bits_[0] |= 0x00000800u; + min_slot_duration_ms_ = value; +} +inline void UwbControleeCapabilities::set_min_slot_duration_ms(float value) { + _internal_set_min_slot_duration_ms(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.min_slot_duration_ms) +} + +// repeated int32 supported_ntf_configs = 13 [packed = true]; +inline int UwbControleeCapabilities::_internal_supported_ntf_configs_size() const { + return supported_ntf_configs_.size(); +} +inline int UwbControleeCapabilities::supported_ntf_configs_size() const { + return _internal_supported_ntf_configs_size(); +} +inline void UwbControleeCapabilities::clear_supported_ntf_configs() { + supported_ntf_configs_.Clear(); +} +inline int32_t UwbControleeCapabilities::_internal_supported_ntf_configs(int index) const { + return supported_ntf_configs_.Get(index); +} +inline int32_t UwbControleeCapabilities::supported_ntf_configs(int index) const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.supported_ntf_configs) + return _internal_supported_ntf_configs(index); +} +inline void UwbControleeCapabilities::set_supported_ntf_configs(int index, int32_t value) { + supported_ntf_configs_.Set(index, value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.supported_ntf_configs) +} +inline void UwbControleeCapabilities::_internal_add_supported_ntf_configs(int32_t value) { + supported_ntf_configs_.Add(value); +} +inline void UwbControleeCapabilities::add_supported_ntf_configs(int32_t value) { + _internal_add_supported_ntf_configs(value); + // @@protoc_insertion_point(field_add:nearby.presence.UwbControleeCapabilities.supported_ntf_configs) +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::_internal_supported_ntf_configs() const { + return supported_ntf_configs_; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::supported_ntf_configs() const { + // @@protoc_insertion_point(field_list:nearby.presence.UwbControleeCapabilities.supported_ntf_configs) + return _internal_supported_ntf_configs(); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::_internal_mutable_supported_ntf_configs() { + return &supported_ntf_configs_; +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::mutable_supported_ntf_configs() { + // @@protoc_insertion_point(field_mutable_list:nearby.presence.UwbControleeCapabilities.supported_ntf_configs) + return _internal_mutable_supported_ntf_configs(); +} + +// optional bool is_ranging_interval_reconfigure_supported = 14 [default = false]; +inline bool UwbControleeCapabilities::_internal_has_is_ranging_interval_reconfigure_supported() const { + bool value = (_has_bits_[0] & 0x00000080u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_is_ranging_interval_reconfigure_supported() const { + return _internal_has_is_ranging_interval_reconfigure_supported(); +} +inline void UwbControleeCapabilities::clear_is_ranging_interval_reconfigure_supported() { + is_ranging_interval_reconfigure_supported_ = false; + _has_bits_[0] &= ~0x00000080u; +} +inline bool UwbControleeCapabilities::_internal_is_ranging_interval_reconfigure_supported() const { + return is_ranging_interval_reconfigure_supported_; +} +inline bool UwbControleeCapabilities::is_ranging_interval_reconfigure_supported() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.is_ranging_interval_reconfigure_supported) + return _internal_is_ranging_interval_reconfigure_supported(); +} +inline void UwbControleeCapabilities::_internal_set_is_ranging_interval_reconfigure_supported(bool value) { + _has_bits_[0] |= 0x00000080u; + is_ranging_interval_reconfigure_supported_ = value; +} +inline void UwbControleeCapabilities::set_is_ranging_interval_reconfigure_supported(bool value) { + _internal_set_is_ranging_interval_reconfigure_supported(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.is_ranging_interval_reconfigure_supported) +} + +// repeated int32 supported_slot_durations = 15 [packed = true]; +inline int UwbControleeCapabilities::_internal_supported_slot_durations_size() const { + return supported_slot_durations_.size(); +} +inline int UwbControleeCapabilities::supported_slot_durations_size() const { + return _internal_supported_slot_durations_size(); +} +inline void UwbControleeCapabilities::clear_supported_slot_durations() { + supported_slot_durations_.Clear(); +} +inline int32_t UwbControleeCapabilities::_internal_supported_slot_durations(int index) const { + return supported_slot_durations_.Get(index); +} +inline int32_t UwbControleeCapabilities::supported_slot_durations(int index) const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.supported_slot_durations) + return _internal_supported_slot_durations(index); +} +inline void UwbControleeCapabilities::set_supported_slot_durations(int index, int32_t value) { + supported_slot_durations_.Set(index, value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.supported_slot_durations) +} +inline void UwbControleeCapabilities::_internal_add_supported_slot_durations(int32_t value) { + supported_slot_durations_.Add(value); +} +inline void UwbControleeCapabilities::add_supported_slot_durations(int32_t value) { + _internal_add_supported_slot_durations(value); + // @@protoc_insertion_point(field_add:nearby.presence.UwbControleeCapabilities.supported_slot_durations) +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::_internal_supported_slot_durations() const { + return supported_slot_durations_; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::supported_slot_durations() const { + // @@protoc_insertion_point(field_list:nearby.presence.UwbControleeCapabilities.supported_slot_durations) + return _internal_supported_slot_durations(); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::_internal_mutable_supported_slot_durations() { + return &supported_slot_durations_; +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::mutable_supported_slot_durations() { + // @@protoc_insertion_point(field_mutable_list:nearby.presence.UwbControleeCapabilities.supported_slot_durations) + return _internal_mutable_supported_slot_durations(); +} + +// repeated int32 supported_ranging_update_rates = 16 [packed = true]; +inline int UwbControleeCapabilities::_internal_supported_ranging_update_rates_size() const { + return supported_ranging_update_rates_.size(); +} +inline int UwbControleeCapabilities::supported_ranging_update_rates_size() const { + return _internal_supported_ranging_update_rates_size(); +} +inline void UwbControleeCapabilities::clear_supported_ranging_update_rates() { + supported_ranging_update_rates_.Clear(); +} +inline int32_t UwbControleeCapabilities::_internal_supported_ranging_update_rates(int index) const { + return supported_ranging_update_rates_.Get(index); +} +inline int32_t UwbControleeCapabilities::supported_ranging_update_rates(int index) const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.supported_ranging_update_rates) + return _internal_supported_ranging_update_rates(index); +} +inline void UwbControleeCapabilities::set_supported_ranging_update_rates(int index, int32_t value) { + supported_ranging_update_rates_.Set(index, value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.supported_ranging_update_rates) +} +inline void UwbControleeCapabilities::_internal_add_supported_ranging_update_rates(int32_t value) { + supported_ranging_update_rates_.Add(value); +} +inline void UwbControleeCapabilities::add_supported_ranging_update_rates(int32_t value) { + _internal_add_supported_ranging_update_rates(value); + // @@protoc_insertion_point(field_add:nearby.presence.UwbControleeCapabilities.supported_ranging_update_rates) +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::_internal_supported_ranging_update_rates() const { + return supported_ranging_update_rates_; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >& +UwbControleeCapabilities::supported_ranging_update_rates() const { + // @@protoc_insertion_point(field_list:nearby.presence.UwbControleeCapabilities.supported_ranging_update_rates) + return _internal_supported_ranging_update_rates(); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::_internal_mutable_supported_ranging_update_rates() { + return &supported_ranging_update_rates_; +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< int32_t >* +UwbControleeCapabilities::mutable_supported_ranging_update_rates() { + // @@protoc_insertion_point(field_mutable_list:nearby.presence.UwbControleeCapabilities.supported_ranging_update_rates) + return _internal_mutable_supported_ranging_update_rates(); +} + +// optional int32 chip_count = 17 [default = 1]; +inline bool UwbControleeCapabilities::_internal_has_chip_count() const { + bool value = (_has_bits_[0] & 0x00000100u) != 0; + return value; +} +inline bool UwbControleeCapabilities::has_chip_count() const { + return _internal_has_chip_count(); +} +inline void UwbControleeCapabilities::clear_chip_count() { + chip_count_ = 1; + _has_bits_[0] &= ~0x00000100u; +} +inline int32_t UwbControleeCapabilities::_internal_chip_count() const { + return chip_count_; +} +inline int32_t UwbControleeCapabilities::chip_count() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.chip_count) + return _internal_chip_count(); +} +inline void UwbControleeCapabilities::_internal_set_chip_count(int32_t value) { + _has_bits_[0] |= 0x00000100u; + chip_count_ = value; +} +inline void UwbControleeCapabilities::set_chip_count(int32_t value) { + _internal_set_chip_count(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbControleeCapabilities.chip_count) +} + +// repeated .nearby.presence.UwbMultiChipInfo multi_chip_info = 18; +inline int UwbControleeCapabilities::_internal_multi_chip_info_size() const { + return multi_chip_info_.size(); +} +inline int UwbControleeCapabilities::multi_chip_info_size() const { + return _internal_multi_chip_info_size(); +} +inline void UwbControleeCapabilities::clear_multi_chip_info() { + multi_chip_info_.Clear(); +} +inline ::nearby::presence::UwbMultiChipInfo* UwbControleeCapabilities::mutable_multi_chip_info(int index) { + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbControleeCapabilities.multi_chip_info) + return multi_chip_info_.Mutable(index); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::nearby::presence::UwbMultiChipInfo >* +UwbControleeCapabilities::mutable_multi_chip_info() { + // @@protoc_insertion_point(field_mutable_list:nearby.presence.UwbControleeCapabilities.multi_chip_info) + return &multi_chip_info_; +} +inline const ::nearby::presence::UwbMultiChipInfo& UwbControleeCapabilities::_internal_multi_chip_info(int index) const { + return multi_chip_info_.Get(index); +} +inline const ::nearby::presence::UwbMultiChipInfo& UwbControleeCapabilities::multi_chip_info(int index) const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbControleeCapabilities.multi_chip_info) + return _internal_multi_chip_info(index); +} +inline ::nearby::presence::UwbMultiChipInfo* UwbControleeCapabilities::_internal_add_multi_chip_info() { + return multi_chip_info_.Add(); +} +inline ::nearby::presence::UwbMultiChipInfo* UwbControleeCapabilities::add_multi_chip_info() { + ::nearby::presence::UwbMultiChipInfo* _add = _internal_add_multi_chip_info(); + // @@protoc_insertion_point(field_add:nearby.presence.UwbControleeCapabilities.multi_chip_info) + return _add; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::nearby::presence::UwbMultiChipInfo >& +UwbControleeCapabilities::multi_chip_info() const { + // @@protoc_insertion_point(field_list:nearby.presence.UwbControleeCapabilities.multi_chip_info) + return multi_chip_info_; +} + +// ------------------------------------------------------------------- + +// UwbMultiChipInfo + +// optional bytes controlee_address = 1; +inline bool UwbMultiChipInfo::_internal_has_controlee_address() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + return value; +} +inline bool UwbMultiChipInfo::has_controlee_address() const { + return _internal_has_controlee_address(); +} +inline void UwbMultiChipInfo::clear_controlee_address() { + controlee_address_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000001u; +} +inline const std::string& UwbMultiChipInfo::controlee_address() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbMultiChipInfo.controlee_address) + return _internal_controlee_address(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void UwbMultiChipInfo::set_controlee_address(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000001u; + controlee_address_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.UwbMultiChipInfo.controlee_address) +} +inline std::string* UwbMultiChipInfo::mutable_controlee_address() { + std::string* _s = _internal_mutable_controlee_address(); + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbMultiChipInfo.controlee_address) + return _s; +} +inline const std::string& UwbMultiChipInfo::_internal_controlee_address() const { + return controlee_address_.Get(); +} +inline void UwbMultiChipInfo::_internal_set_controlee_address(const std::string& value) { + _has_bits_[0] |= 0x00000001u; + controlee_address_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* UwbMultiChipInfo::_internal_mutable_controlee_address() { + _has_bits_[0] |= 0x00000001u; + return controlee_address_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* UwbMultiChipInfo::release_controlee_address() { + // @@protoc_insertion_point(field_release:nearby.presence.UwbMultiChipInfo.controlee_address) + if (!_internal_has_controlee_address()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000001u; + auto* p = controlee_address_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (controlee_address_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + controlee_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void UwbMultiChipInfo::set_allocated_controlee_address(std::string* controlee_address) { + if (controlee_address != nullptr) { + _has_bits_[0] |= 0x00000001u; + } else { + _has_bits_[0] &= ~0x00000001u; + } + controlee_address_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), controlee_address, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (controlee_address_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + controlee_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.UwbMultiChipInfo.controlee_address) +} + +// optional string chip_id = 2; +inline bool UwbMultiChipInfo::_internal_has_chip_id() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool UwbMultiChipInfo::has_chip_id() const { + return _internal_has_chip_id(); +} +inline void UwbMultiChipInfo::clear_chip_id() { + chip_id_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000002u; +} +inline const std::string& UwbMultiChipInfo::chip_id() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbMultiChipInfo.chip_id) + return _internal_chip_id(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void UwbMultiChipInfo::set_chip_id(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000002u; + chip_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.UwbMultiChipInfo.chip_id) +} +inline std::string* UwbMultiChipInfo::mutable_chip_id() { + std::string* _s = _internal_mutable_chip_id(); + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbMultiChipInfo.chip_id) + return _s; +} +inline const std::string& UwbMultiChipInfo::_internal_chip_id() const { + return chip_id_.Get(); +} +inline void UwbMultiChipInfo::_internal_set_chip_id(const std::string& value) { + _has_bits_[0] |= 0x00000002u; + chip_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* UwbMultiChipInfo::_internal_mutable_chip_id() { + _has_bits_[0] |= 0x00000002u; + return chip_id_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* UwbMultiChipInfo::release_chip_id() { + // @@protoc_insertion_point(field_release:nearby.presence.UwbMultiChipInfo.chip_id) + if (!_internal_has_chip_id()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000002u; + auto* p = chip_id_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (chip_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + chip_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void UwbMultiChipInfo::set_allocated_chip_id(std::string* chip_id) { + if (chip_id != nullptr) { + _has_bits_[0] |= 0x00000002u; + } else { + _has_bits_[0] &= ~0x00000002u; + } + chip_id_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), chip_id, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (chip_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + chip_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.UwbMultiChipInfo.chip_id) +} + +// ------------------------------------------------------------------- + +// UwbConnectionInfo + +// optional bytes controller_address = 1; +inline bool UwbConnectionInfo::_internal_has_controller_address() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_controller_address() const { + return _internal_has_controller_address(); +} +inline void UwbConnectionInfo::clear_controller_address() { + controller_address_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000001u; +} +inline const std::string& UwbConnectionInfo::controller_address() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.controller_address) + return _internal_controller_address(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void UwbConnectionInfo::set_controller_address(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000001u; + controller_address_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.controller_address) +} +inline std::string* UwbConnectionInfo::mutable_controller_address() { + std::string* _s = _internal_mutable_controller_address(); + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbConnectionInfo.controller_address) + return _s; +} +inline const std::string& UwbConnectionInfo::_internal_controller_address() const { + return controller_address_.Get(); +} +inline void UwbConnectionInfo::_internal_set_controller_address(const std::string& value) { + _has_bits_[0] |= 0x00000001u; + controller_address_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* UwbConnectionInfo::_internal_mutable_controller_address() { + _has_bits_[0] |= 0x00000001u; + return controller_address_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* UwbConnectionInfo::release_controller_address() { + // @@protoc_insertion_point(field_release:nearby.presence.UwbConnectionInfo.controller_address) + if (!_internal_has_controller_address()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000001u; + auto* p = controller_address_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (controller_address_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + controller_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void UwbConnectionInfo::set_allocated_controller_address(std::string* controller_address) { + if (controller_address != nullptr) { + _has_bits_[0] |= 0x00000001u; + } else { + _has_bits_[0] &= ~0x00000001u; + } + controller_address_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), controller_address, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (controller_address_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + controller_address_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.UwbConnectionInfo.controller_address) +} + +// optional int32 channel = 2; +inline bool UwbConnectionInfo::_internal_has_channel() const { + bool value = (_has_bits_[0] & 0x00000010u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_channel() const { + return _internal_has_channel(); +} +inline void UwbConnectionInfo::clear_channel() { + channel_ = 0; + _has_bits_[0] &= ~0x00000010u; +} +inline int32_t UwbConnectionInfo::_internal_channel() const { + return channel_; +} +inline int32_t UwbConnectionInfo::channel() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.channel) + return _internal_channel(); +} +inline void UwbConnectionInfo::_internal_set_channel(int32_t value) { + _has_bits_[0] |= 0x00000010u; + channel_ = value; +} +inline void UwbConnectionInfo::set_channel(int32_t value) { + _internal_set_channel(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.channel) +} + +// optional int32 preamble_index = 3; +inline bool UwbConnectionInfo::_internal_has_preamble_index() const { + bool value = (_has_bits_[0] & 0x00000020u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_preamble_index() const { + return _internal_has_preamble_index(); +} +inline void UwbConnectionInfo::clear_preamble_index() { + preamble_index_ = 0; + _has_bits_[0] &= ~0x00000020u; +} +inline int32_t UwbConnectionInfo::_internal_preamble_index() const { + return preamble_index_; +} +inline int32_t UwbConnectionInfo::preamble_index() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.preamble_index) + return _internal_preamble_index(); +} +inline void UwbConnectionInfo::_internal_set_preamble_index(int32_t value) { + _has_bits_[0] |= 0x00000020u; + preamble_index_ = value; +} +inline void UwbConnectionInfo::set_preamble_index(int32_t value) { + _internal_set_preamble_index(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.preamble_index) +} + +// optional int32 config_id = 4; +inline bool UwbConnectionInfo::_internal_has_config_id() const { + bool value = (_has_bits_[0] & 0x00000040u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_config_id() const { + return _internal_has_config_id(); +} +inline void UwbConnectionInfo::clear_config_id() { + config_id_ = 0; + _has_bits_[0] &= ~0x00000040u; +} +inline int32_t UwbConnectionInfo::_internal_config_id() const { + return config_id_; +} +inline int32_t UwbConnectionInfo::config_id() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.config_id) + return _internal_config_id(); +} +inline void UwbConnectionInfo::_internal_set_config_id(int32_t value) { + _has_bits_[0] |= 0x00000040u; + config_id_ = value; +} +inline void UwbConnectionInfo::set_config_id(int32_t value) { + _internal_set_config_id(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.config_id) +} + +// optional int32 ranging_interval_ms = 5; +inline bool UwbConnectionInfo::_internal_has_ranging_interval_ms() const { + bool value = (_has_bits_[0] & 0x00000080u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_ranging_interval_ms() const { + return _internal_has_ranging_interval_ms(); +} +inline void UwbConnectionInfo::clear_ranging_interval_ms() { + ranging_interval_ms_ = 0; + _has_bits_[0] &= ~0x00000080u; +} +inline int32_t UwbConnectionInfo::_internal_ranging_interval_ms() const { + return ranging_interval_ms_; +} +inline int32_t UwbConnectionInfo::ranging_interval_ms() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.ranging_interval_ms) + return _internal_ranging_interval_ms(); +} +inline void UwbConnectionInfo::_internal_set_ranging_interval_ms(int32_t value) { + _has_bits_[0] |= 0x00000080u; + ranging_interval_ms_ = value; +} +inline void UwbConnectionInfo::set_ranging_interval_ms(int32_t value) { + _internal_set_ranging_interval_ms(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.ranging_interval_ms) +} + +// optional int32 session_id = 6; +inline bool UwbConnectionInfo::_internal_has_session_id() const { + bool value = (_has_bits_[0] & 0x00000100u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_session_id() const { + return _internal_has_session_id(); +} +inline void UwbConnectionInfo::clear_session_id() { + session_id_ = 0; + _has_bits_[0] &= ~0x00000100u; +} +inline int32_t UwbConnectionInfo::_internal_session_id() const { + return session_id_; +} +inline int32_t UwbConnectionInfo::session_id() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.session_id) + return _internal_session_id(); +} +inline void UwbConnectionInfo::_internal_set_session_id(int32_t value) { + _has_bits_[0] |= 0x00000100u; + session_id_ = value; +} +inline void UwbConnectionInfo::set_session_id(int32_t value) { + _internal_set_session_id(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.session_id) +} + +// optional bytes vendor_id = 7; +inline bool UwbConnectionInfo::_internal_has_vendor_id() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_vendor_id() const { + return _internal_has_vendor_id(); +} +inline void UwbConnectionInfo::clear_vendor_id() { + vendor_id_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000002u; +} +inline const std::string& UwbConnectionInfo::vendor_id() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.vendor_id) + return _internal_vendor_id(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void UwbConnectionInfo::set_vendor_id(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000002u; + vendor_id_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.vendor_id) +} +inline std::string* UwbConnectionInfo::mutable_vendor_id() { + std::string* _s = _internal_mutable_vendor_id(); + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbConnectionInfo.vendor_id) + return _s; +} +inline const std::string& UwbConnectionInfo::_internal_vendor_id() const { + return vendor_id_.Get(); +} +inline void UwbConnectionInfo::_internal_set_vendor_id(const std::string& value) { + _has_bits_[0] |= 0x00000002u; + vendor_id_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* UwbConnectionInfo::_internal_mutable_vendor_id() { + _has_bits_[0] |= 0x00000002u; + return vendor_id_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* UwbConnectionInfo::release_vendor_id() { + // @@protoc_insertion_point(field_release:nearby.presence.UwbConnectionInfo.vendor_id) + if (!_internal_has_vendor_id()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000002u; + auto* p = vendor_id_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (vendor_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + vendor_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void UwbConnectionInfo::set_allocated_vendor_id(std::string* vendor_id) { + if (vendor_id != nullptr) { + _has_bits_[0] |= 0x00000002u; + } else { + _has_bits_[0] &= ~0x00000002u; + } + vendor_id_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), vendor_id, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (vendor_id_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + vendor_id_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.UwbConnectionInfo.vendor_id) +} + +// optional bytes static_sts_iv = 8; +inline bool UwbConnectionInfo::_internal_has_static_sts_iv() const { + bool value = (_has_bits_[0] & 0x00000004u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_static_sts_iv() const { + return _internal_has_static_sts_iv(); +} +inline void UwbConnectionInfo::clear_static_sts_iv() { + static_sts_iv_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000004u; +} +inline const std::string& UwbConnectionInfo::static_sts_iv() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.static_sts_iv) + return _internal_static_sts_iv(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void UwbConnectionInfo::set_static_sts_iv(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000004u; + static_sts_iv_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.static_sts_iv) +} +inline std::string* UwbConnectionInfo::mutable_static_sts_iv() { + std::string* _s = _internal_mutable_static_sts_iv(); + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbConnectionInfo.static_sts_iv) + return _s; +} +inline const std::string& UwbConnectionInfo::_internal_static_sts_iv() const { + return static_sts_iv_.Get(); +} +inline void UwbConnectionInfo::_internal_set_static_sts_iv(const std::string& value) { + _has_bits_[0] |= 0x00000004u; + static_sts_iv_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* UwbConnectionInfo::_internal_mutable_static_sts_iv() { + _has_bits_[0] |= 0x00000004u; + return static_sts_iv_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* UwbConnectionInfo::release_static_sts_iv() { + // @@protoc_insertion_point(field_release:nearby.presence.UwbConnectionInfo.static_sts_iv) + if (!_internal_has_static_sts_iv()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000004u; + auto* p = static_sts_iv_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (static_sts_iv_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + static_sts_iv_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void UwbConnectionInfo::set_allocated_static_sts_iv(std::string* static_sts_iv) { + if (static_sts_iv != nullptr) { + _has_bits_[0] |= 0x00000004u; + } else { + _has_bits_[0] &= ~0x00000004u; + } + static_sts_iv_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), static_sts_iv, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (static_sts_iv_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + static_sts_iv_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.UwbConnectionInfo.static_sts_iv) +} + +// optional bytes session_key = 9; +inline bool UwbConnectionInfo::_internal_has_session_key() const { + bool value = (_has_bits_[0] & 0x00000008u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_session_key() const { + return _internal_has_session_key(); +} +inline void UwbConnectionInfo::clear_session_key() { + session_key_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000008u; +} +inline const std::string& UwbConnectionInfo::session_key() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.session_key) + return _internal_session_key(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void UwbConnectionInfo::set_session_key(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000008u; + session_key_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.session_key) +} +inline std::string* UwbConnectionInfo::mutable_session_key() { + std::string* _s = _internal_mutable_session_key(); + // @@protoc_insertion_point(field_mutable:nearby.presence.UwbConnectionInfo.session_key) + return _s; +} +inline const std::string& UwbConnectionInfo::_internal_session_key() const { + return session_key_.Get(); +} +inline void UwbConnectionInfo::_internal_set_session_key(const std::string& value) { + _has_bits_[0] |= 0x00000008u; + session_key_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* UwbConnectionInfo::_internal_mutable_session_key() { + _has_bits_[0] |= 0x00000008u; + return session_key_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* UwbConnectionInfo::release_session_key() { + // @@protoc_insertion_point(field_release:nearby.presence.UwbConnectionInfo.session_key) + if (!_internal_has_session_key()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000008u; + auto* p = session_key_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (session_key_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + session_key_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void UwbConnectionInfo::set_allocated_session_key(std::string* session_key) { + if (session_key != nullptr) { + _has_bits_[0] |= 0x00000008u; + } else { + _has_bits_[0] &= ~0x00000008u; + } + session_key_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), session_key, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (session_key_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + session_key_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.UwbConnectionInfo.session_key) +} + +// optional bool ranging_disabled = 10; +inline bool UwbConnectionInfo::_internal_has_ranging_disabled() const { + bool value = (_has_bits_[0] & 0x00000200u) != 0; + return value; +} +inline bool UwbConnectionInfo::has_ranging_disabled() const { + return _internal_has_ranging_disabled(); +} +inline void UwbConnectionInfo::clear_ranging_disabled() { + ranging_disabled_ = false; + _has_bits_[0] &= ~0x00000200u; +} +inline bool UwbConnectionInfo::_internal_ranging_disabled() const { + return ranging_disabled_; +} +inline bool UwbConnectionInfo::ranging_disabled() const { + // @@protoc_insertion_point(field_get:nearby.presence.UwbConnectionInfo.ranging_disabled) + return _internal_ranging_disabled(); +} +inline void UwbConnectionInfo::_internal_set_ranging_disabled(bool value) { + _has_bits_[0] |= 0x00000200u; + ranging_disabled_ = value; +} +inline void UwbConnectionInfo::set_ranging_disabled(bool value) { + _internal_set_ranging_disabled(value); + // @@protoc_insertion_point(field_set:nearby.presence.UwbConnectionInfo.ranging_disabled) +} + +// ------------------------------------------------------------------- + +// ControlFrame + +// optional .nearby.presence.ControlFrame.ControlType type = 1; +inline bool ControlFrame::_internal_has_type() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + return value; +} +inline bool ControlFrame::has_type() const { + return _internal_has_type(); +} +inline void ControlFrame::clear_type() { + type_ = 0; + _has_bits_[0] &= ~0x00000001u; +} +inline ::nearby::presence::ControlFrame_ControlType ControlFrame::_internal_type() const { + return static_cast< ::nearby::presence::ControlFrame_ControlType >(type_); +} +inline ::nearby::presence::ControlFrame_ControlType ControlFrame::type() const { + // @@protoc_insertion_point(field_get:nearby.presence.ControlFrame.type) + return _internal_type(); +} +inline void ControlFrame::_internal_set_type(::nearby::presence::ControlFrame_ControlType value) { + assert(::nearby::presence::ControlFrame_ControlType_IsValid(value)); + _has_bits_[0] |= 0x00000001u; + type_ = value; +} +inline void ControlFrame::set_type(::nearby::presence::ControlFrame_ControlType value) { + _internal_set_type(value); + // @@protoc_insertion_point(field_set:nearby.presence.ControlFrame.type) +} + +// ------------------------------------------------------------------- + +// PresenceAuthenticationFrame + +// optional int32 version = 1; +inline bool PresenceAuthenticationFrame::_internal_has_version() const { + bool value = (_has_bits_[0] & 0x00000008u) != 0; + return value; +} +inline bool PresenceAuthenticationFrame::has_version() const { + return _internal_has_version(); +} +inline void PresenceAuthenticationFrame::clear_version() { + version_ = 0; + _has_bits_[0] &= ~0x00000008u; +} +inline int32_t PresenceAuthenticationFrame::_internal_version() const { + return version_; +} +inline int32_t PresenceAuthenticationFrame::version() const { + // @@protoc_insertion_point(field_get:nearby.presence.PresenceAuthenticationFrame.version) + return _internal_version(); +} +inline void PresenceAuthenticationFrame::_internal_set_version(int32_t value) { + _has_bits_[0] |= 0x00000008u; + version_ = value; +} +inline void PresenceAuthenticationFrame::set_version(int32_t value) { + _internal_set_version(value); + // @@protoc_insertion_point(field_set:nearby.presence.PresenceAuthenticationFrame.version) +} + +// optional bytes private_key_signature = 2; +inline bool PresenceAuthenticationFrame::_internal_has_private_key_signature() const { + bool value = (_has_bits_[0] & 0x00000001u) != 0; + return value; +} +inline bool PresenceAuthenticationFrame::has_private_key_signature() const { + return _internal_has_private_key_signature(); +} +inline void PresenceAuthenticationFrame::clear_private_key_signature() { + private_key_signature_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000001u; +} +inline const std::string& PresenceAuthenticationFrame::private_key_signature() const { + // @@protoc_insertion_point(field_get:nearby.presence.PresenceAuthenticationFrame.private_key_signature) + return _internal_private_key_signature(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PresenceAuthenticationFrame::set_private_key_signature(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000001u; + private_key_signature_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.PresenceAuthenticationFrame.private_key_signature) +} +inline std::string* PresenceAuthenticationFrame::mutable_private_key_signature() { + std::string* _s = _internal_mutable_private_key_signature(); + // @@protoc_insertion_point(field_mutable:nearby.presence.PresenceAuthenticationFrame.private_key_signature) + return _s; +} +inline const std::string& PresenceAuthenticationFrame::_internal_private_key_signature() const { + return private_key_signature_.Get(); +} +inline void PresenceAuthenticationFrame::_internal_set_private_key_signature(const std::string& value) { + _has_bits_[0] |= 0x00000001u; + private_key_signature_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* PresenceAuthenticationFrame::_internal_mutable_private_key_signature() { + _has_bits_[0] |= 0x00000001u; + return private_key_signature_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* PresenceAuthenticationFrame::release_private_key_signature() { + // @@protoc_insertion_point(field_release:nearby.presence.PresenceAuthenticationFrame.private_key_signature) + if (!_internal_has_private_key_signature()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000001u; + auto* p = private_key_signature_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (private_key_signature_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + private_key_signature_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void PresenceAuthenticationFrame::set_allocated_private_key_signature(std::string* private_key_signature) { + if (private_key_signature != nullptr) { + _has_bits_[0] |= 0x00000001u; + } else { + _has_bits_[0] &= ~0x00000001u; + } + private_key_signature_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), private_key_signature, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (private_key_signature_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + private_key_signature_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.PresenceAuthenticationFrame.private_key_signature) +} + +// optional bytes shared_credential_id_hash = 3; +inline bool PresenceAuthenticationFrame::_internal_has_shared_credential_id_hash() const { + bool value = (_has_bits_[0] & 0x00000002u) != 0; + return value; +} +inline bool PresenceAuthenticationFrame::has_shared_credential_id_hash() const { + return _internal_has_shared_credential_id_hash(); +} +inline void PresenceAuthenticationFrame::clear_shared_credential_id_hash() { + shared_credential_id_hash_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000002u; +} +inline const std::string& PresenceAuthenticationFrame::shared_credential_id_hash() const { + // @@protoc_insertion_point(field_get:nearby.presence.PresenceAuthenticationFrame.shared_credential_id_hash) + return _internal_shared_credential_id_hash(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PresenceAuthenticationFrame::set_shared_credential_id_hash(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000002u; + shared_credential_id_hash_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.PresenceAuthenticationFrame.shared_credential_id_hash) +} +inline std::string* PresenceAuthenticationFrame::mutable_shared_credential_id_hash() { + std::string* _s = _internal_mutable_shared_credential_id_hash(); + // @@protoc_insertion_point(field_mutable:nearby.presence.PresenceAuthenticationFrame.shared_credential_id_hash) + return _s; +} +inline const std::string& PresenceAuthenticationFrame::_internal_shared_credential_id_hash() const { + return shared_credential_id_hash_.Get(); +} +inline void PresenceAuthenticationFrame::_internal_set_shared_credential_id_hash(const std::string& value) { + _has_bits_[0] |= 0x00000002u; + shared_credential_id_hash_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* PresenceAuthenticationFrame::_internal_mutable_shared_credential_id_hash() { + _has_bits_[0] |= 0x00000002u; + return shared_credential_id_hash_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* PresenceAuthenticationFrame::release_shared_credential_id_hash() { + // @@protoc_insertion_point(field_release:nearby.presence.PresenceAuthenticationFrame.shared_credential_id_hash) + if (!_internal_has_shared_credential_id_hash()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000002u; + auto* p = shared_credential_id_hash_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (shared_credential_id_hash_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + shared_credential_id_hash_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void PresenceAuthenticationFrame::set_allocated_shared_credential_id_hash(std::string* shared_credential_id_hash) { + if (shared_credential_id_hash != nullptr) { + _has_bits_[0] |= 0x00000002u; + } else { + _has_bits_[0] &= ~0x00000002u; + } + shared_credential_id_hash_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), shared_credential_id_hash, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (shared_credential_id_hash_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + shared_credential_id_hash_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.PresenceAuthenticationFrame.shared_credential_id_hash) +} + +// optional bytes credential_id_hash = 4 [deprecated = true]; +inline bool PresenceAuthenticationFrame::_internal_has_credential_id_hash() const { + bool value = (_has_bits_[0] & 0x00000004u) != 0; + return value; +} +inline bool PresenceAuthenticationFrame::has_credential_id_hash() const { + return _internal_has_credential_id_hash(); +} +inline void PresenceAuthenticationFrame::clear_credential_id_hash() { + credential_id_hash_.ClearToEmpty(); + _has_bits_[0] &= ~0x00000004u; +} +inline const std::string& PresenceAuthenticationFrame::credential_id_hash() const { + // @@protoc_insertion_point(field_get:nearby.presence.PresenceAuthenticationFrame.credential_id_hash) + return _internal_credential_id_hash(); +} +template +inline PROTOBUF_ALWAYS_INLINE +void PresenceAuthenticationFrame::set_credential_id_hash(ArgT0&& arg0, ArgT... args) { + _has_bits_[0] |= 0x00000004u; + credential_id_hash_.SetBytes(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, static_cast(arg0), args..., GetArenaForAllocation()); + // @@protoc_insertion_point(field_set:nearby.presence.PresenceAuthenticationFrame.credential_id_hash) +} +inline std::string* PresenceAuthenticationFrame::mutable_credential_id_hash() { + std::string* _s = _internal_mutable_credential_id_hash(); + // @@protoc_insertion_point(field_mutable:nearby.presence.PresenceAuthenticationFrame.credential_id_hash) + return _s; +} +inline const std::string& PresenceAuthenticationFrame::_internal_credential_id_hash() const { + return credential_id_hash_.Get(); +} +inline void PresenceAuthenticationFrame::_internal_set_credential_id_hash(const std::string& value) { + _has_bits_[0] |= 0x00000004u; + credential_id_hash_.Set(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, value, GetArenaForAllocation()); +} +inline std::string* PresenceAuthenticationFrame::_internal_mutable_credential_id_hash() { + _has_bits_[0] |= 0x00000004u; + return credential_id_hash_.Mutable(::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr::EmptyDefault{}, GetArenaForAllocation()); +} +inline std::string* PresenceAuthenticationFrame::release_credential_id_hash() { + // @@protoc_insertion_point(field_release:nearby.presence.PresenceAuthenticationFrame.credential_id_hash) + if (!_internal_has_credential_id_hash()) { + return nullptr; + } + _has_bits_[0] &= ~0x00000004u; + auto* p = credential_id_hash_.ReleaseNonDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (credential_id_hash_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + credential_id_hash_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + return p; +} +inline void PresenceAuthenticationFrame::set_allocated_credential_id_hash(std::string* credential_id_hash) { + if (credential_id_hash != nullptr) { + _has_bits_[0] |= 0x00000004u; + } else { + _has_bits_[0] &= ~0x00000004u; + } + credential_id_hash_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), credential_id_hash, + GetArenaForAllocation()); +#ifdef PROTOBUF_FORCE_COPY_DEFAULT_STRING + if (credential_id_hash_.IsDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited())) { + credential_id_hash_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), "", GetArenaForAllocation()); + } +#endif // PROTOBUF_FORCE_COPY_DEFAULT_STRING + // @@protoc_insertion_point(field_set_allocated:nearby.presence.PresenceAuthenticationFrame.credential_id_hash) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + +// ------------------------------------------------------------------- + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace presence +} // namespace nearby + +PROTOBUF_NAMESPACE_OPEN + +template <> struct is_proto_enum< ::nearby::presence::PresenceFrame_Version> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::nearby::presence::PresenceFrame_Version>() { + return ::nearby::presence::PresenceFrame_Version_descriptor(); +} +template <> struct is_proto_enum< ::nearby::presence::ControlFrame_ControlType> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::nearby::presence::ControlFrame_ControlType>() { + return ::nearby::presence::ControlFrame_ControlType_descriptor(); +} + +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_presence_2fproto_2fpresence_5fframe_2eproto From 034865481a970ae02d280d615b26d0e926465469 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Tue, 22 Aug 2023 11:35:54 -0700 Subject: [PATCH 122/128] Sanitize incoming file names PiperOrigin-RevId: 559175545 --- .../platform/implementation/apple/Tests/BUILD | 1 + .../apple/Tests/GNCPlatformTest.mm | 83 +++++++++++++++++++ .../platform/implementation/apple/platform.mm | 40 +++++---- 3 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 internal/platform/implementation/apple/Tests/GNCPlatformTest.mm diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index 2ca87d00..9cc10150 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -42,6 +42,7 @@ objc_library( "GNCFakePeripheralManager.m", "GNCIPAddressTest.mm", "GNCMultiThreadExecutorTest.mm", + "GNCPlatformTest.mm", "GNCScheduledExecutorTest.mm", "GNCSingleThreadExecutorTest.mm", "GNCUtilsTest.mm", diff --git a/internal/platform/implementation/apple/Tests/GNCPlatformTest.mm b/internal/platform/implementation/apple/Tests/GNCPlatformTest.mm new file mode 100644 index 00000000..322d0725 --- /dev/null +++ b/internal/platform/implementation/apple/Tests/GNCPlatformTest.mm @@ -0,0 +1,83 @@ +// 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/platform.h" + +#import +#import + +void GNCEnsureFileAtPath(std::string path) { + [NSFileManager.defaultManager + createDirectoryAtPath:[@(path.c_str()) stringByDeletingLastPathComponent] + withIntermediateDirectories:YES + attributes:nil + error:nil]; + [[NSData data] writeToFile:@(path.c_str()) options:0 error:nil]; +} + +@interface GNCPlatformTest : XCTestCase +@end + +@implementation GNCPlatformTest + +- (void)testGetCustomSavePath { + NSString *expected = [NSURL fileURLWithPath:@"a/b/c.d"].path; + std::string actual = nearby::api::ImplementationPlatform::GetCustomSavePath("a/b", "c.d"); + XCTAssertEqualObjects(@(actual.c_str()), expected); +} + +- (void)testGetCustomSavePathWithIllegalCharacters { + NSString *expected = [NSURL fileURLWithPath:@"a/b/Fi?le*: Name.ext"].path; + std::string actual = + nearby::api::ImplementationPlatform::GetCustomSavePath("a/b", "Fi?le*/ Name.ext"); + XCTAssertEqualObjects(@(actual.c_str()), expected); +} + +- (void)testGetCustomSavePathWithPathEscapingCharacters { + NSString *expected = [NSURL fileURLWithPath:@"a/b/..:c:..:d.e"].path; + std::string actual = + nearby::api::ImplementationPlatform::GetCustomSavePath("a/../../b", "../c/../d.e"); + XCTAssertEqualObjects(@(actual.c_str()), expected); +} + +- (void)testGetCustomSavePathDuplicateNames { + NSString *expected1 = [NSURL fileURLWithPath:@"a/b/cat.jpg"].path; + NSString *expected2 = [NSURL fileURLWithPath:@"a/b/cat 2.jpg"].path; + NSString *expected3 = [NSURL fileURLWithPath:@"a/b/cat 3.jpg"].path; + + std::string actual1 = nearby::api::ImplementationPlatform::GetCustomSavePath("a/b", "cat.jpg"); + GNCEnsureFileAtPath(actual1); + + std::string actual2 = nearby::api::ImplementationPlatform::GetCustomSavePath("a/b", "cat.jpg"); + GNCEnsureFileAtPath(actual2); + + std::string actual3 = nearby::api::ImplementationPlatform::GetCustomSavePath("a/b", "cat.jpg"); + + // Cleanup created files. + [NSFileManager.defaultManager removeItemAtPath:@(actual1.c_str()) error:nil]; + [NSFileManager.defaultManager removeItemAtPath:@(actual2.c_str()) error:nil]; + + XCTAssertEqualObjects(@(actual1.c_str()), expected1); + XCTAssertEqualObjects(@(actual2.c_str()), expected2); + XCTAssertEqualObjects(@(actual3.c_str()), expected3); +} + +- (void)testGetDownloadPath { + NSString *expected = + [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:@"a/b/c.d"].path; + std::string actual = nearby::api::ImplementationPlatform::GetDownloadPath("a/b", "c.d"); + XCTAssertEqualObjects(@(actual.c_str()), expected); +} + +@end diff --git a/internal/platform/implementation/apple/platform.mm b/internal/platform/implementation/apple/platform.mm index f23a112b..b70fc53a 100644 --- a/internal/platform/implementation/apple/platform.mm +++ b/internal/platform/implementation/apple/platform.mm @@ -44,37 +44,45 @@ namespace api { std::string ImplementationPlatform::GetCustomSavePath(const std::string& parent_folder, const std::string& file_name) { - NSFileManager* manager = [NSFileManager defaultManager]; + // Collapse any path escaping characters. + NSString* parentFolder = [@(parent_folder.c_str()) stringByReplacingOccurrencesOfString:@"../" + withString:@""]; + NSURL* parentFolderURL = [NSURL fileURLWithPath:parentFolder]; - NSURL* parentFolder = [NSURL fileURLWithPath:@(parent_folder.c_str())]; - - NSString* fileName = @(file_name.c_str()); + // The only reserved character in a file name on macOS is the forward-slash. It's unclear if iOS + // has any additional restrictions. + // + // """ + // In the Finder, filenames containing `/` can be created, but `/` is stored as a colon (:) in the + // filesystem, and is shown as such on the command line. Filenames containing `:` created from the + // command line are shown with `/` instead of `:` in the Finder, so that it is impossible to + // create a file that the Finder shows as having a `:` in its filename. + // """ + // + // See: https://en.wikipedia.org/wiki/Filename + NSString* fileName = [@(file_name.c_str()) stringByReplacingOccurrencesOfString:@"/" + withString:@":"]; NSString* baseName = [fileName stringByDeletingPathExtension]; NSString* extension = [fileName pathExtension]; - NSURL* url = [parentFolder URLByAppendingPathComponent:fileName]; + NSURL* url = [parentFolderURL URLByAppendingPathComponent:fileName]; NSInteger index = 1; - while ([manager fileExistsAtPath:url.path]) { + while ([NSFileManager.defaultManager fileExistsAtPath:url.path]) { index++; NSString* fileName = [NSString stringWithFormat:@"%@ %@.%@", baseName, [@(index) stringValue], extension]; - url = [parentFolder URLByAppendingPathComponent:fileName]; + url = [parentFolderURL URLByAppendingPathComponent:fileName]; } - return [url.path UTF8String]; + return url.path.UTF8String; } std::string ImplementationPlatform::GetDownloadPath(const std::string& parent_folder, const std::string& file_name) { - // TODO(jfcarroll): This needs to be done correctly, we now have a file name and parent folder, - // they should be combined with the default download path - NSString* fileName = ObjCStringFromCppString(file_name); - - // TODO(b/227535777): If file name matches an existing file, it will be overwritten. Append a - // number until a unique file name is reached 'foobar (2).png'. - - return CppStringFromObjCString([NSTemporaryDirectory() stringByAppendingPathComponent:fileName]); + NSString* customSavePath = + [NSTemporaryDirectory() stringByAppendingPathComponent:@(parent_folder.c_str())]; + return GetCustomSavePath(customSavePath.UTF8String, file_name); } OSName ImplementationPlatform::GetCurrentOS() { return OSName::kApple; } From 2c4bf0471f1d3a3876c3ac6f15984564e653cac7 Mon Sep 17 00:00:00 2001 From: Anay Wadhera Date: Tue, 22 Aug 2023 20:37:05 -0700 Subject: [PATCH 123/128] change connections v3 API surface to use the new advertising/discovery options PiperOrigin-RevId: 559300673 --- connections/core.cc | 152 +++++++++++++++++++++++---------------- connections/core.h | 12 ++-- connections/core_test.cc | 82 +++++++++++++++++++-- 3 files changed, 172 insertions(+), 74 deletions(-) diff --git a/connections/core.cc b/connections/core.cc index 00a50701..2dbb44a2 100644 --- a/connections/core.cc +++ b/connections/core.cc @@ -19,15 +19,32 @@ #include #include "absl/strings/string_view.h" -#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "absl/types/span.h" +#include "connections/advertising_options.h" +#include "connections/connection_options.h" +#include "connections/discovery_options.h" +#include "connections/implementation/service_controller_router.h" #include "connections/implementation/service_id_constants.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/payload_type.h" +#include "connections/power_level.h" +#include "connections/status.h" +#include "connections/v3/advertising_options.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/discovery_options.h" #include "connections/v3/listeners.h" +#include "connections/v3/listening_result.h" #include "connections/v3/params.h" #include "internal/interop/device.h" +#include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/feature_flags.h" #include "internal/platform/logging.h" @@ -182,7 +199,7 @@ std::string Core::Dump() { return client_.Dump(); } // V3 void Core::StartAdvertisingV3(absl::string_view service_id, - const AdvertisingOptions& advertising_options, + const v3::AdvertisingOptions& advertising_options, const NearbyDevice& local_device, v3::ConnectionListener listener, ResultCallback callback) { @@ -238,56 +255,32 @@ void Core::StartAdvertisingV3(absl::string_view service_id, .endpoint_info = local_endpoint_info, .listener = old_listener, }; - StartAdvertising(service_id, advertising_options, old_info, - std::move(callback)); + + CheckServiceId(service_id); + CHECK(advertising_options.strategy.IsValid()); + AdvertisingOptions old_advertising_options = { + { + advertising_options.strategy, + advertising_options.advertising_mediums, + }, + advertising_options.auto_upgrade_bandwidth, + advertising_options.enforce_topology_constraints, + advertising_options.power_level == PowerLevel::kLowPower, // low_power + advertising_options.enable_bluetooth_listening, + advertising_options.advertising_mediums.web_rtc, + false, // is_out_of_band_connection + advertising_options.fast_advertisement_service_uuid, + "" // device_info + }; + // TODO(b/291295755): Refactor deeper to use v3 options throughout. + router_->StartAdvertising(&client_, service_id, old_advertising_options, + old_info, std::move(callback)); } void Core::StartAdvertisingV3(absl::string_view service_id, - const AdvertisingOptions& advertising_options, + const v3::AdvertisingOptions& advertising_options, v3::ConnectionListener listener, ResultCallback callback) { - ConnectionListener old_listener = { - .initiated_cb = - [&listener](const std::string& endpoint_id, - const ConnectionResponseInfo& info) { - auto remote_device = v3::ConnectionsDevice( - endpoint_id, info.remote_endpoint_info.AsStringView(), {}); - listener.initiated_cb( - remote_device, - v3::InitialConnectionInfo{ - .authentication_digits = info.authentication_token, - .raw_authentication_token = - info.raw_authentication_token.string_data(), - .is_incoming_connection = info.is_incoming_connection, - }); - }, - .accepted_cb = - [v3_cb = listener.result_cb](const std::string& endpoint_id) { - auto remote_device = v3::ConnectionsDevice(endpoint_id, "", {}); - v3_cb(remote_device, - v3::ConnectionResult{.status = Status{ - .value = Status::kSuccess, - }}); - }, - .rejected_cb = - [v3_cb = listener.result_cb](const std::string& endpoint_id, - Status status) { - auto remote_device = v3::ConnectionsDevice(endpoint_id, "", {}); - v3_cb(remote_device, v3::ConnectionResult{ - .status = status, - }); - }, - .disconnected_cb = - [&listener](const std::string& endpoint_id) { - auto remote_device = v3::ConnectionsDevice(endpoint_id, "", {}); - listener.disconnected_cb(remote_device); - }, - .bandwidth_changed_cb = - [&listener](const std::string& endpoint_id, Medium medium) { - auto remote_device = v3::ConnectionsDevice(endpoint_id, "", {}); - listener.bandwidth_changed_cb(remote_device, - v3::BandwidthInfo{.medium = medium}); - }}; ByteArray local_endpoint_info; const NearbyDevice* local_device = client_.GetLocalDevice(); if (local_device->GetType() == NearbyDevice::kConnectionsDevice) { @@ -295,12 +288,8 @@ void Core::StartAdvertisingV3(absl::string_view service_id, ByteArray(reinterpret_cast(local_device) ->GetEndpointInfo()); } - ConnectionRequestInfo old_info = { - .endpoint_info = local_endpoint_info, - .listener = old_listener, - }; - StartAdvertising(service_id, advertising_options, old_info, - std::move(callback)); + StartAdvertisingV3(service_id, advertising_options, *local_device, + std::move(listener), std::move(callback)); } void Core::StopAdvertisingV3(ResultCallback result_cb) { @@ -308,7 +297,7 @@ void Core::StopAdvertisingV3(ResultCallback result_cb) { } void Core::StartDiscoveryV3(absl::string_view service_id, - const DiscoveryOptions& discovery_options, + const v3::DiscoveryOptions& discovery_options, v3::DiscoveryListener listener, ResultCallback callback) { DiscoveryListener old_listener = { @@ -332,7 +321,19 @@ void Core::StartDiscoveryV3(absl::string_view service_id, listener.endpoint_distance_changed_cb(remote, distance_info); }, }; - StartDiscovery(service_id, discovery_options, old_listener, + DiscoveryOptions old_discovery_options = { + { + discovery_options.strategy, + discovery_options.discovery_mediums, + }, + true, // auto_upgrade_bandwidth + true, // enforce_topology_constraints + false, // is_out_of_band_connection + discovery_options.fast_advertisement_service_uuid, + discovery_options.power_level == PowerLevel::kLowPower, + }; + // TODO(b/291295755): Deeper refactor to use v3 options throughout. + StartDiscovery(service_id, old_discovery_options, old_listener, std::move(callback)); } @@ -469,17 +470,44 @@ void Core::InitiateBandwidthUpgradeV3(const NearbyDevice& remote_device, std::move(result_cb)); } -void Core::UpdateAdvertisingOptionsV3(absl::string_view service_id, - AdvertisingOptions advertising_options, - ResultCallback result_cb) { - router_->UpdateAdvertisingOptionsV3(&client_, service_id, advertising_options, - std::move(result_cb)); +void Core::UpdateAdvertisingOptionsV3( + absl::string_view service_id, v3::AdvertisingOptions advertising_options, + ResultCallback result_cb) { + // TODO(b/291295755): Deeper refactor to use new advertising options. + AdvertisingOptions old_advertising_options = { + { + advertising_options.strategy, + advertising_options.advertising_mediums, + }, + advertising_options.auto_upgrade_bandwidth, + advertising_options.enforce_topology_constraints, + advertising_options.power_level == PowerLevel::kLowPower, // low_power + advertising_options.enable_bluetooth_listening, + advertising_options.advertising_mediums.web_rtc, + false, // is_out_of_band_connection + advertising_options.fast_advertisement_service_uuid, + "" // device_info + }; + router_->UpdateAdvertisingOptionsV3( + &client_, service_id, old_advertising_options, std::move(result_cb)); } void Core::UpdateDiscoveryOptionsV3(absl::string_view service_id, - DiscoveryOptions discovery_options, + v3::DiscoveryOptions discovery_options, ResultCallback result_cb) { - router_->UpdateDiscoveryOptionsV3(&client_, service_id, discovery_options, + // TODO(b/291295755): Deeper refactor to use new discovery options. + DiscoveryOptions old_discovery_options = { + { + discovery_options.strategy, + discovery_options.discovery_mediums, + }, + true, // auto_upgrade_bandwidth + true, // enforce_topology_constraints + false, // is_out_of_band_connection + discovery_options.fast_advertisement_service_uuid, + discovery_options.power_level == PowerLevel::kLowPower, + }; + router_->UpdateDiscoveryOptionsV3(&client_, service_id, old_discovery_options, std::move(result_cb)); } diff --git a/connections/core.h b/connections/core.h index f58c3962..7cf6fcc8 100644 --- a/connections/core.h +++ b/connections/core.h @@ -28,7 +28,9 @@ #include "connections/listeners.h" #include "connections/params.h" #include "connections/payload.h" +#include "connections/v3/advertising_options.h" #include "connections/v3/connection_listening_options.h" +#include "connections/v3/discovery_options.h" #include "connections/v3/listeners.h" #include "connections/v3/listening_result.h" #include "internal/analytics/event_logger.h" @@ -273,7 +275,7 @@ class Core { // Status::STATUS_OUT_OF_ORDER_API_CALL if the app is currently // connected to remote endpoints; call StopAllEndpoints first. void StartAdvertisingV3(absl::string_view service_id, - const AdvertisingOptions& advertising_options, + const v3::AdvertisingOptions& advertising_options, const NearbyDevice& local_device, v3::ConnectionListener listener, ResultCallback callback); @@ -296,7 +298,7 @@ class Core { // Status::STATUS_OUT_OF_ORDER_API_CALL if the app is currently // connected to remote endpoints; call StopAllEndpoints first. void StartAdvertisingV3(absl::string_view service_id, - const AdvertisingOptions& advertising_options, + const v3::AdvertisingOptions& advertising_options, v3::ConnectionListener listener, ResultCallback callback); @@ -322,7 +324,7 @@ class Core { // Status::STATUS_OUT_OF_ORDER_API_CALL if the app is currently // connected to remote endpoints; call StopAllEndpoints first. void StartDiscoveryV3(absl::string_view service_id, - const DiscoveryOptions& discovery_options, + const v3::DiscoveryOptions& discovery_options, v3::DiscoveryListener listener_cb, ResultCallback callback); @@ -504,7 +506,7 @@ class Core { // advertising_options - The new advertising options a client wishes to use. // result_cb - to access the status of the operation when available. void UpdateAdvertisingOptionsV3(absl::string_view service_id, - AdvertisingOptions advertising_options, + v3::AdvertisingOptions advertising_options, ResultCallback result_cb); // Updates DiscoveryOptions. It compares the old DiscoveryOptions and the new @@ -513,7 +515,7 @@ class Core { // discovery_options - The new discovery options a client wishes to use. // result_cb - to access the status of the operation when available. void UpdateDiscoveryOptionsV3(absl::string_view service_id, - DiscoveryOptions discovery_options, + v3::DiscoveryOptions discovery_options, ResultCallback result_cb); // Registers a DeviceProvider to provide functionality for Nearby Connections diff --git a/connections/core_test.cc b/connections/core_test.cc index cd5d75d1..5456963c 100644 --- a/connections/core_test.cc +++ b/connections/core_test.cc @@ -14,16 +14,29 @@ #include "connections/core.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 "absl/time/clock.h" +#include "connections/advertising_options.h" +#include "connections/discovery_options.h" #include "connections/implementation/mock_service_controller_router.h" +#include "connections/listeners.h" +#include "connections/medium_selector.h" +#include "connections/params.h" +#include "connections/payload.h" +#include "connections/power_level.h" +#include "connections/status.h" +#include "connections/strategy.h" +#include "connections/v3/advertising_options.h" #include "connections/v3/bandwidth_info.h" +#include "connections/v3/connection_result.h" #include "connections/v3/connections_device.h" +#include "connections/v3/discovery_options.h" +#include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/logging.h" @@ -107,6 +120,61 @@ TEST(CoreTest, SendPayloadCallsScRouter) { core.SendPayload({"TEST"}, Payload(ByteArray("Hello world")), {}); } +TEST(CoreV3Test, TestAdvertisingOptionsConversionWorks) { + MockServiceControllerRouter mock; + // Called when Core is destroyed. + EXPECT_CALL(mock, StopAllEndpoints) + .WillOnce([&](ClientProxy* client, ResultCallback callback) { + callback({Status::kSuccess}); + }); + EXPECT_CALL(mock, StartAdvertising) + .WillOnce([](ClientProxy*, absl::string_view, + const AdvertisingOptions& options, + const ConnectionRequestInfo& info, ResultCallback) { + EXPECT_EQ(options.strategy, Strategy::kP2pCluster); + EXPECT_FALSE(options.low_power); + EXPECT_TRUE(options.enable_bluetooth_listening); + EXPECT_FALSE(options.auto_upgrade_bandwidth); + EXPECT_EQ(options.fast_advertisement_service_uuid, "NearbyConnections"); + }); + Core core{&mock}; + v3::AdvertisingOptions advertising_options = { + .strategy = Strategy::kP2pCluster, + .power_level = PowerLevel::kHighPower, + .enable_bluetooth_listening = true, + .auto_upgrade_bandwidth = false, + .fast_advertisement_service_uuid = "NearbyConnections", + }; + core.StartAdvertisingV3("service", advertising_options, {}, {}); + core.StopDiscoveryV3({}); +} + +TEST(CoreV3Test, TestDiscoveryOptionsConversionWorks) { + MockServiceControllerRouter mock; + // Called when Core is destroyed. + EXPECT_CALL(mock, StopAllEndpoints) + .WillOnce([&](ClientProxy* client, ResultCallback callback) { + callback({Status::kSuccess}); + }); + EXPECT_CALL(mock, StartDiscovery) + .WillOnce([](ClientProxy*, absl::string_view, + const DiscoveryOptions& options, + const DiscoveryListener& info, ResultCallback) { + EXPECT_EQ(options.strategy, Strategy::kP2pCluster); + EXPECT_FALSE(options.low_power); + EXPECT_TRUE(options.auto_upgrade_bandwidth); + EXPECT_EQ(options.fast_advertisement_service_uuid, "NearbyConnections"); + }); + Core core{&mock}; + v3::DiscoveryOptions discovery_options = { + .strategy = Strategy::kP2pCluster, + .power_level = PowerLevel::kHighPower, + .fast_advertisement_service_uuid = "NearbyConnections", + }; + core.StartDiscoveryV3("service", discovery_options, {}, {}); + core.StopDiscoveryV3({}); +} + TEST(CoreV3Test, TestCallbackWrapWorksStartAdvertisingV3FourArgs) { MockServiceControllerRouter mock; EXPECT_CALL(mock, StartAdvertising) @@ -131,7 +199,7 @@ TEST(CoreV3Test, TestCallbackWrapWorksStartAdvertisingV3FourArgs) { CountDownLatch bandwidth_changed_latch(1); CountDownLatch disconnected_latch(1); CountDownLatch initiated_latch(1); - AdvertisingOptions advertising_options; + v3::AdvertisingOptions advertising_options; advertising_options.strategy = Strategy::kP2pCluster; core.StartAdvertisingV3( "service", advertising_options, @@ -188,7 +256,7 @@ TEST(CoreV3Test, TestStartAdvertisingV3NonConnectionsDeviceProvider) { CountDownLatch bandwidth_changed_latch(1); CountDownLatch disconnected_latch(1); CountDownLatch initiated_latch(1); - AdvertisingOptions advertising_options; + v3::AdvertisingOptions advertising_options; advertising_options.strategy = Strategy::kP2pCluster; FakeNearbyDeviceProvider device_provider; core.RegisterDeviceProvider(&device_provider); @@ -247,7 +315,7 @@ TEST(CoreV3Test, TestStartAdvertisingV3NonConnectionsDevice) { CountDownLatch bandwidth_changed_latch(1); CountDownLatch disconnected_latch(1); CountDownLatch initiated_latch(1); - AdvertisingOptions advertising_options; + v3::AdvertisingOptions advertising_options; advertising_options.strategy = Strategy::kP2pCluster; auto local_device = FakeNearbyDevice(); core.StartAdvertisingV3( @@ -305,7 +373,7 @@ TEST(CoreV3Test, TestCallbackWrapWorksStartAdvertisingV3FiveArgs) { CountDownLatch bandwidth_changed_latch(1); CountDownLatch disconnected_latch(1); CountDownLatch initiated_latch(1); - AdvertisingOptions advertising_options; + v3::AdvertisingOptions advertising_options; advertising_options.strategy = Strategy::kP2pCluster; auto local_device = v3::ConnectionsDevice("FAKE", "endpoint_info", {}); core.StartAdvertisingV3( @@ -355,7 +423,7 @@ TEST(CoreV3Test, TestCallbackWrapWorksStartDiscoveryV3) { NEARBY_LOGS(INFO) << "StopAllEndpoints called"; callback({Status::kSuccess}); }); - DiscoveryOptions options; + v3::DiscoveryOptions options; options.strategy = Strategy::kP2pCluster; Core core{&mock}; CountDownLatch endpoint_distance_latch(1); From 387393f1d6b9e0cdf3387704df70970bf016d5e9 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Wed, 23 Aug 2023 12:53:12 -0700 Subject: [PATCH 124/128] Delete old BLE code for Apple platforms PiperOrigin-RevId: 559515626 --- Package.swift | 8 - internal/platform/implementation/apple/BUILD | 46 +- .../implementation/apple/Mediums/BUILD | 6 - .../apple/Mediums/Ble/GNCMBleCentral.h | 114 ---- .../apple/Mediums/Ble/GNCMBleCentral.m | 510 --------------- .../apple/Mediums/Ble/GNCMBlePeripheral.h | 79 --- .../apple/Mediums/Ble/GNCMBlePeripheral.m | 293 --------- .../platform/implementation/apple/Tests/BUILD | 6 +- .../implementation/apple/Tests/GNCBleTest.mm | 112 ---- .../apple/Tests/GNCBluetoothAdapterTest.mm | 87 --- internal/platform/implementation/apple/ble.h | 219 +------ internal/platform/implementation/apple/ble.mm | 600 ------------------ .../implementation/apple/ble_gatt_server.h | 2 +- .../implementation/apple/bluetooth_adapter.h | 108 ---- .../implementation/apple/bluetooth_adapter.mm | 28 - .../platform/implementation/apple/platform.mm | 2 +- 16 files changed, 20 insertions(+), 2200 deletions(-) delete mode 100644 internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h delete mode 100644 internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.m delete mode 100644 internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h delete mode 100644 internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.m delete mode 100644 internal/platform/implementation/apple/Tests/GNCBleTest.mm delete mode 100644 internal/platform/implementation/apple/Tests/GNCBluetoothAdapterTest.mm delete mode 100644 internal/platform/implementation/apple/ble.mm delete mode 100644 internal/platform/implementation/apple/bluetooth_adapter.h delete mode 100644 internal/platform/implementation/apple/bluetooth_adapter.mm diff --git a/Package.swift b/Package.swift index ca4e39e6..e9ceacdc 100644 --- a/Package.swift +++ b/Package.swift @@ -570,14 +570,6 @@ let package = Package( "connections/implementation/mediums/webrtc", // This breaks the build, but seems to work fine without it? "internal/platform/medium_environment.cc", - // Temporarily ignore BLEv2 source files. - // TODO(b/293283024): Stop ignoring these files when BLEv2 migration is complete. - "internal/platform/implementation/apple/ble_gatt_server.mm", - "internal/platform/implementation/apple/ble_gatt_client.mm", - "internal/platform/implementation/apple/ble_medium.mm", - "internal/platform/implementation/apple/ble_peripheral.mm", - "internal/platform/implementation/apple/ble_server_socket.mm", - "internal/platform/implementation/apple/ble_socket.mm", ], sources: [ "compiled_proto", diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 252b664b..0e2b044e 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -23,7 +23,6 @@ package(default_visibility = [ objc_library( name = "apple", srcs = [ - "ble_utils.mm", "crypto.mm", "device_info.mm", "log_message.mm", @@ -35,7 +34,6 @@ objc_library( "wifi_lan.mm", ], hdrs = [ - "ble_utils.h", "device_info.h", "log_message.h", "multi_thread_executor.h", @@ -51,7 +49,7 @@ objc_library( deps = [ ":Platform_cc", ":Shared", - ":ble", + ":ble_v2", "//internal/platform:base", "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", @@ -77,55 +75,29 @@ objc_library( }), ) -objc_library( - name = "ble", - srcs = [ - "ble.mm", - "bluetooth_adapter.mm", - "utils.mm", - ], - hdrs = [ - "ble.h", - "bluetooth_adapter.h", - "utils.h", - ], - # Prevent Objective-C++ headers from being pulled into swift. - aspect_hints = ["//tools/build_defs/swift:no_module"], - deps = [ - "//internal/platform:base", - "//internal/platform:cancellation_flag", - "//internal/platform/implementation:comm", - "//internal/platform/implementation/apple/Mediums", - "//third_party/apple_frameworks:CoreBluetooth", - "//third_party/apple_frameworks:Foundation", - "@com_google_absl//absl/container:flat_hash_map", - "@com_google_absl//absl/strings", - "@com_google_absl//absl/types:optional", - ], -) - objc_library( name = "ble_v2", srcs = [ + "ble_gatt_client.h", "ble_gatt_client.mm", + "ble_gatt_server.h", "ble_gatt_server.mm", + "ble_medium.h", "ble_medium.mm", + "ble_peripheral.h", "ble_peripheral.mm", + "ble_server_socket.h", "ble_server_socket.mm", + "ble_socket.h", "ble_socket.mm", "ble_utils.mm", + "bluetooth_adapter_v2.h", "bluetooth_adapter_v2.mm", "utils.mm", ], hdrs = [ - "ble_gatt_client.h", - "ble_gatt_server.h", - "ble_medium.h", - "ble_peripheral.h", - "ble_server_socket.h", - "ble_socket.h", + "ble.h", "ble_utils.h", - "bluetooth_adapter_v2.h", "utils.h", ], # Prevent Objective-C++ headers from being pulled into swift. diff --git a/internal/platform/implementation/apple/Mediums/BUILD b/internal/platform/implementation/apple/Mediums/BUILD index d9f5917d..21f1c10d 100644 --- a/internal/platform/implementation/apple/Mediums/BUILD +++ b/internal/platform/implementation/apple/Mediums/BUILD @@ -27,9 +27,7 @@ objc_library( "BLEv2/GNCPeripheral.m", "BLEv2/GNCPeripheralManager.m", "BLEv2/NSData+GNCWebSafeBase64.m", - "Ble/GNCMBleCentral.m", "Ble/GNCMBleConnection.m", - "Ble/GNCMBlePeripheral.m", "Ble/GNCMBleUtils.mm", "GNCLeaks.h", "GNCLeaks.m", @@ -51,9 +49,7 @@ objc_library( "BLEv2/GNCPeripheral.h", "BLEv2/GNCPeripheralManager.h", "BLEv2/NSData+GNCWebSafeBase64.h", - "Ble/GNCMBleCentral.h", "Ble/GNCMBleConnection.h", - "Ble/GNCMBlePeripheral.h", "Ble/GNCMBleUtils.h", "GNCMConnection.h", "WiFiLAN/GNCIPv4Address.h", @@ -64,8 +60,6 @@ objc_library( ], deps = [ "//internal/platform/implementation/apple:Shared", - "//internal/platform/implementation/apple/Mediums/Ble/Sockets:Central", - "//internal/platform/implementation/apple/Mediums/Ble/Sockets:Peripheral", "//internal/platform/implementation/apple/Mediums/Ble/Sockets:Shared", "//proto/mediums:ble_frames_cc_proto", "//third_party/apple_frameworks:CoreBluetooth", diff --git a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h b/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h deleted file mode 100644 index d617113f..00000000 --- a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h +++ /dev/null @@ -1,114 +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. - -#import - -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" - -@class CBUUID; - -NS_ASSUME_NONNULL_BEGIN - -/** - * This handler is called on a discover when a nearby advertising endpoint is discovered. - */ -typedef void (^GNCMScanResultHandler)(NSString *peripheralID, NSData *serviceData); - -/** - * This handler is called on a discovery when a nearby advertising endpoint is connected. - */ -typedef void (^GNCMGATTConnectionResultHandler)(NSError *_Nullable error); - -/** - * This handler is called on a discovery when a nearby advertising endpoint is connected. The input - * is a discovered set of characteristics. - */ -typedef void (^GNCMGATTDiscoverResultHandler)( - NSOrderedSet *_Nullable characteristicValues); - -/** - * This handler is called by a discovering endpoint to request a connection with an an advertising - * endpoint. - */ -typedef void (^GNCMBleConnectionRequester)(NSString *serviceID, - GNCMConnectionHandler connectionHandler); - -/** - * This handler is called on a discoverer when a nearby advertising endpoint is discovered. - * Call |requestConnection| to request a connection with the advertiser. - */ -typedef void (^GNCMBleRequestConnectionHandler)(GNCMBleConnectionRequester requestConnection); - -/** - * GNCMBleCentral discovers devices advertising the specified service UUID via BLE (using the - * GNCMBlePeripheral class) and calls the specififed scanning result handler when one is found. - * - * This class is thread-safe. Any calls made to it (and to the objects/closures it passes back via - * callbacks) can be made from any thread/queue. Callbacks made from this class are called on the - * specified queue. - */ -@interface GNCMBleCentral : NSObject - -- (instancetype)init NS_DESIGNATED_INITIALIZER; - -/** - * Starts scanning with service UUID. - * - * @param serviceUUID A string that uniquely identifies the scanning services to search for. - * @param scanResultHandler The handler that is called when an endpoint advertising the service - * UUID is discovered. - * @param requestConnectionHandler The handler that is called when an endpoint is discovered. - * @param callbackQueue The queue on which all callbacks are made. - */ -- (BOOL)startScanningWithServiceUUID:(NSString *)serviceUUID - scanResultHandler:(GNCMScanResultHandler)scanResultHandler - requestConnectionHandler:(GNCMBleRequestConnectionHandler)requestConnectionHandler - callbackQueue:(nullable dispatch_queue_t)callbackQueue; - -/** - * Sets up a GATT connection. - * - * @param peripheralID A string that uniquely identifies the peripheral. - * @param gattConnectionResultHandler The handler that is called when an endpoint is - * connected. - */ -- (void)connectGattServerWithPeripheralID:(NSString *)peripheralID - gattConnectionResultHandler: - (GNCMGATTConnectionResultHandler)gattConnectionResultHandler; - -/** - * Discovers GATT service and its associated characteristics with values. - * - * @param serviceUUID A CBUUID for service to discover. - * @param gattCharacteristics Array of CBUUID for characteristic to discover. - * @param peripheralID A string that uniquely identifies the peripheral. - * @param gattDiscoverResultHandler This handler is called on a discovery for a discovered map of - * characteristic values when a nearby advertising endpoint is - * connected. - */ -- (void)discoverGattService:(CBUUID *)serviceUUID - gattCharacteristics:(NSArray *)characteristicUUIDs - peripheralID:(NSString *)peripheralID - gattDiscoverResultHandler:(GNCMGATTDiscoverResultHandler)gattDiscoverResultHandler; - -/** - * Disconnects GATT connection. - * - * @param peripheralID A string that uniquely identifies the peripheral. - */ -- (void)disconnectGattServiceWithPeripheralID:(NSString *)peripheralID; - -@end - -NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.m b/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.m deleted file mode 100644 index bc5ea3aa..00000000 --- a/internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.m +++ /dev/null @@ -1,510 +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. - -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h" - -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleConnection.h" -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.h" -#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Central/GNSCentralManager.h" -#import "internal/platform/implementation/apple/Mediums/Ble/Sockets/Source/Central/GNSCentralPeerManager.h" -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, GNCMCentralState) { - GNCMCentralStateStopped, - GNCMCentralStateScanning, -}; - -typedef void (^GNCMBleCharacteristicsHandler)(NSArray *characteristics, - NSError *error); -typedef void (^GNCMBleCharacteristicValueHandler)(CBCharacteristic *characteristic, NSError *error); -typedef void (^GNCIntHandler)(int); - -/** This lets a GNCIntHandler call itself. */ -GNCIntHandler GNCRecursiveIntHandler(void (^block)(GNCIntHandler blockSelf, int i)) { - return ^(int i) { - return block(GNCRecursiveIntHandler(block), i); - }; -} - -/** This represents a discovered peripheral. */ -@interface GNCMPeripheralInfo : NSObject -@property(nonatomic) CBPeripheral *peripheral; -@property(nonatomic, copy) NSDictionary *advertisementData; - -/** Called when characteristics are discovered. */ -@property(nonatomic, nullable) GNCMBleCharacteristicsHandler charsHandler; - -/** Called when a characteristic value is read. */ -@property(nonatomic, nullable) GNCMBleCharacteristicValueHandler charValueHandler; - -@end - -@implementation GNCMPeripheralInfo - -- (instancetype)initWithPeripheral:(CBPeripheral *)peripheral - advertisementData:(NSDictionary *)advertisementData { - self = [super init]; - if (self) { - _peripheral = peripheral; - _advertisementData = [advertisementData copy]; - } - return self; -} - -- (BOOL)isEqual:(id)object { - // There is always exactly one info object per peripheral, so compare by identity. This is needed - // for maintenance of the peripherals stored in multiple maps. - return self == object; -} - -- (NSUInteger)hash { - return (NSUInteger)self; -} - -@end - -@interface GNCMBleCentral () -@end - -@implementation GNCMBleCentral { - /** Service UUID the central is scanning for. */ - CBUUID *_serviceUUID; - /** The scan result callback handler. */ - GNCMScanResultHandler _scanResultHandler; - /** Central manager used to scan or connect to peripherals. */ - CBCentralManager *_centralManager; - /** Serial background queue for |centralManager|. */ - dispatch_queue_t _selfQueue; - /** Central state for stop or scanning. */ - GNCMCentralState _centralState; - /** The dictionary keyed by CBPeripheral identifier to value GNCMPeripheralInfo. */ - NSMutableDictionary *_nearbyPeripheralsByID; - /** Array of characteristic UUID used for discovering. */ - NSArray *_characteristicUUIDs; - /** GATT connection result handler. */ - GNCMGATTConnectionResultHandler _gattConnectionResultHandler; - /** GATT service and characteristic discovery result hanadler. */ - GNCMGATTDiscoverResultHandler _gattDiscoverResultHandler; - /** The discovered characteristic values map used to callback for `_gattDiscoverResultHandler`. */ - NSMutableOrderedSet *_gattCharacteristicValues; - /** Central manager used for socket connection based on weave protocol. */ - GNSCentralManager *_socketCentralManager; - /** A callback handler to reuqest connection on the discovered advertiser. */ - GNCMBleRequestConnectionHandler _requestConnectionHandler; - /** Client callback queue. If client doesn't assign it, then use main queue. */ - dispatch_queue_t _clientCallbackQueue; - /** Internal async priority queue. */ - dispatch_queue_t _internalCallbackQueue; - /** Flag to disable callback for dealloc. */ - BOOL _callbacksEnabled; -} - -- (instancetype)init { - if (self = [super init]) { - // To make this class thread-safe, use a serial queue for all state changes, and have Core - // Bluetooth also use this queue. - _selfQueue = dispatch_queue_create("GNCCentralManagerQueue", DISPATCH_QUEUE_SERIAL); - - _nearbyPeripheralsByID = [NSMutableDictionary dictionary]; - _gattCharacteristicValues = [[NSMutableOrderedSet alloc] init]; - - _centralState = GNCMCentralStateStopped; - } - return self; -} - -- (void)dealloc { - // These calls must be made on |selfQueue|. Can't capture |self| in an async block, so must use - // dispatch_sync. This means dealloc must be called from an external queue, which means |self| - // must never be captured by any escaping block used in this class. - dispatch_sync(_selfQueue, ^{ - [self stopScanningInternal]; - [_socketCentralManager stopNoScanMode]; - - _callbacksEnabled = NO; - }); -} - -- (BOOL)startScanningWithServiceUUID:(NSString *)serviceUUID - scanResultHandler:(GNCMScanResultHandler)scanResultHandler - requestConnectionHandler:(GNCMBleRequestConnectionHandler)requestConnectionHandler - callbackQueue:(nullable dispatch_queue_t)callbackQueue { - NSLog(@"[NEARBY] Client rquests startScanning"); - _serviceUUID = [CBUUID UUIDWithString:serviceUUID]; - _scanResultHandler = scanResultHandler; - _requestConnectionHandler = requestConnectionHandler; - - // The client may be using the callback queue for other purposes, so wrap it with a private - // queue to know with certainty when all callbacks are done. - _clientCallbackQueue = callbackQueue ?: dispatch_get_main_queue(); - _internalCallbackQueue = - dispatch_queue_create("GNCMBleCentralCallbackQueue", DISPATCH_QUEUE_PRIORITY_DEFAULT); - _callbacksEnabled = YES; - - // Set up the central manager for scanning. - _centralManager = - [[CBCentralManager alloc] initWithDelegate:self - queue:_selfQueue - options:@{CBCentralManagerOptionShowPowerAlertKey : @NO}]; - - // Set up the central manager for the socket. - _socketCentralManager = [[GNSCentralManager alloc] initWithSocketServiceUUID:_serviceUUID - queue:_selfQueue]; - _socketCentralManager.delegate = self; - [_socketCentralManager startNoScanModeWithAdvertisedServiceUUIDs:@[ _serviceUUID ]]; - - _centralState = GNCMCentralStateScanning; - return YES; -} - -- (void)connectGattServerWithPeripheralID:(NSString *)peripheralID - gattConnectionResultHandler: - (GNCMGATTConnectionResultHandler)gattConnectionResultHandler { - _gattConnectionResultHandler = gattConnectionResultHandler; - dispatch_sync(_selfQueue, ^{ - GNCMPeripheralInfo *peripheralInfo = - _nearbyPeripheralsByID[[[NSUUID alloc] initWithUUIDString:peripheralID]]; - if (!peripheralInfo) return; - [_centralManager connectPeripheral:peripheralInfo.peripheral options:nil]; - }); -} - -- (void)discoverGattService:(CBUUID *)serviceUUID - gattCharacteristics:(NSArray *)characteristicUUIDs - peripheralID:(NSString *)peripheralID - gattDiscoverResultHandler:(GNCMGATTDiscoverResultHandler)gattDiscoverResultHandler { - _gattDiscoverResultHandler = gattDiscoverResultHandler; - [_gattCharacteristicValues removeAllObjects]; - dispatch_sync(_selfQueue, ^{ - GNCMPeripheralInfo *peripheralInfo = - _nearbyPeripheralsByID[[[NSUUID alloc] initWithUUIDString:peripheralID]]; - if (!peripheralInfo) return; - _characteristicUUIDs = [characteristicUUIDs copy]; - - // Start to discover service and the delegate will get its characteristics and read their values - // recursively - [peripheralInfo.peripheral discoverServices:@[ serviceUUID ]]; - }); -} - -- (void)disconnectGattServiceWithPeripheralID:(NSString *)peripheralID { - dispatch_sync(_selfQueue, ^{ - GNCMPeripheralInfo *peripheralInfo = - _nearbyPeripheralsByID[[[NSUUID alloc] initWithUUIDString:peripheralID]]; - if (!peripheralInfo) return; - _gattConnectionResultHandler = nil; - _gattDiscoverResultHandler = nil; - [_centralManager cancelPeripheralConnection:peripheralInfo.peripheral]; - }); -} - -#pragma mark CBCentralManagerDelegate - -- (void)centralManagerDidUpdateState:(CBCentralManager *)central { - if (central.state == CBManagerStatePoweredOn && !central.isScanning && - _centralState == GNCMCentralStateScanning) { - NSLog(@"[NEARBY] CBCentralManager powered on; starting scan"); - [self startScanningInternal]; - } else { - NSLog(@"[NEARBY] CBCentralManager not powered on; stopping scan"); - [self stopScanningInternal]; - } -} - -- (void)centralManager:(CBCentralManager *)central - didDiscoverPeripheral:(CBPeripheral *)peripheral - advertisementData:(NSDictionary *)advertisementData - RSSI:(NSNumber *)RSSI { - NSNumber *connectable = advertisementData[CBAdvertisementDataIsConnectable]; - if (![connectable boolValue]) return; - - // Look for the NC advertisement header in either the service data (from non-iOS) or the - // advertised name (from iOS). - NSData *serviceData = advertisementData[CBAdvertisementDataServiceDataKey][_serviceUUID] - ?: advertisementData[CBAdvertisementDataLocalNameKey]; - - // Try to look up the peripheral by ID. - GNCMPeripheralInfo *info = _nearbyPeripheralsByID[peripheral.identifier]; - if (!info) { - NSLog(@"[NEARBY] New peripheral: %@", peripheral); - // This is a new peripheral, so create a new peripheral info object. - info = [[GNCMPeripheralInfo alloc] initWithPeripheral:peripheral - advertisementData:advertisementData]; - } else { - info.peripheral = peripheral; - } - - _nearbyPeripheralsByID[peripheral.identifier] = info; - _scanResultHandler(peripheral.identifier.UUIDString, serviceData); -} - -- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { - NSLog(@"[NEARBY] Connected to peripheral: %@", peripheral); - peripheral.delegate = self; - // Tell the caller the connection is done. - _gattConnectionResultHandler(nil); -} - -- (void)centralManager:(CBCentralManager *)central - didFailToConnectPeripheral:(CBPeripheral *)peripheral - error:(nullable NSError *)error { - NSLog(@"[NEARBY] Failed to connect to peripheral: %@, error: %@", peripheral, error); - // Tell the caller the connection failed. - _gattConnectionResultHandler(error); -} - -- (void)centralManager:(CBCentralManager *)central - didDisconnectPeripheral:(CBPeripheral *)peripheral - error:(nullable NSError *)error { - NSLog(@"[NEARBY] Disconnected to peripheral: %@, error: %@", peripheral, error); -} - -#pragma mark CBPeripheralDelegate - -- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error { - GNCMPeripheralInfo *peripheralInfo = _nearbyPeripheralsByID[peripheral.identifier]; - if (!peripheralInfo) return; - if (error || (peripheral.services.count == 0)) { - NSLog(@"[NEARBY] Error reading advertisement: unable to discover services."); - _gattDiscoverResultHandler(nil); - } else { - NSLog(@"[NEARBY] Discovered services for %@: %@", peripheral.name, peripheral.services); - - // Helper functions for discovering characteristics and reading their values. - void (^discoverChars)(CBService *, GNCMBleCharacteristicsHandler) = - ^(CBService *service, GNCMBleCharacteristicsHandler handler) { - NSAssert(!peripheralInfo.charsHandler, @"Unexpected characteristic handler"); - peripheralInfo.charsHandler = handler; - - // Discover all characteristics that may contain the advertisement. - [peripheral discoverCharacteristics:_characteristicUUIDs forService:service]; - }; - void (^readCharValue)(CBCharacteristic *, GNCMBleCharacteristicValueHandler) = - ^(CBCharacteristic *characteristic, GNCMBleCharacteristicValueHandler handler) { - NSAssert(!peripheralInfo.charValueHandler, @"Unexpected characteristic value handler"); - peripheralInfo.charValueHandler = handler; - [peripheral readValueForCharacteristic:characteristic]; - }; - - // Multiple services may have the same UUID, so find the right service by searching for the - // characteristic containing the advertisement with a matching service ID hash. - __weak __typeof__(self) weakSelf = self; - void (^tryService)(int) = GNCRecursiveIntHandler(^(GNCIntHandler tryService, int serviceIndex) { - __strong __typeof__(self) strongSelf = weakSelf; - if (!strongSelf) return; - - // We've tried all the services, report the discovered characteristics and their values. - if (serviceIndex == peripheral.services.count) { - NSLog(@"[NEARBY] Done traversing all services or no services to traverse."); - _gattDiscoverResultHandler(_gattCharacteristicValues); - if (_gattCharacteristicValues.count > 0) { - CBCharacteristic *characteristic = [_gattCharacteristicValues objectAtIndex:0]; - [strongSelf completeGATTReadWithData:characteristic.value - forPeripheralInfo:peripheralInfo]; - } - return; - } - - NSLog(@"[NEARBY] Trying service: %@", peripheral.services[serviceIndex]); - discoverChars( - peripheral.services[serviceIndex], ^(NSArray *chars, NSError *error) { - void (^tryNextService)() = ^{ - tryService(serviceIndex + 1); - }; - - // If there was an error or there are no characteristics on this service, try next one. - if (error || (chars.count == 0)) { - tryNextService(); - return; - } - - // Read each characteristic. - void (^tryChar)(int) = GNCRecursiveIntHandler(^(GNCIntHandler tryChar, int charIndex) { - // We've tried all characteristics on this service without error, try next service. - if (charIndex == chars.count) { - NSLog(@"[NEARBY] No matching advertisement found"); - tryNextService(); - return; - } - - NSLog(@"[NEARBY] Trying characteristic: %@", chars[charIndex]); - readCharValue(chars[charIndex], ^(CBCharacteristic *characteristic, NSError *error) { - if (error) { - tryNextService(); - } else { - // We've found the characteristic and its non-nil value. Store it. - if (characteristic.value.length != 0) { - [_gattCharacteristicValues addObject:characteristic]; - } - tryChar(charIndex + 1); - } - }); - }); - - // Start searching the characteristics for the current service. - tryChar(0); - }); - }); - - // Start searching the services. - tryService(0); - } -} - -- (void)peripheral:(CBPeripheral *)peripheral - didDiscoverCharacteristicsForService:(CBService *)service - error:(nullable NSError *)error { - NSLog(@"[NEARBY] Discovered characteristics: %@ error: %@", service.characteristics, error); - GNCMPeripheralInfo *peripheralInfo = _nearbyPeripheralsByID[peripheral.identifier]; - if (!peripheralInfo) return; - GNCMBleCharacteristicsHandler charsHandler = peripheralInfo.charsHandler; - peripheralInfo.charsHandler = nil; - charsHandler(service.characteristics, error); -} - -- (void)peripheral:(CBPeripheral *)peripheral - didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic - error:(nullable NSError *)error { - NSLog(@"[NEARBY] Read characteristic value: %@ error: %@", characteristic, error); - GNCMPeripheralInfo *peripheralInfo = _nearbyPeripheralsByID[peripheral.identifier]; - if (!peripheralInfo) return; - GNCMBleCharacteristicValueHandler valueHandler = peripheralInfo.charValueHandler; - peripheralInfo.charValueHandler = nil; - valueHandler(characteristic, error); -} - -#pragma mark GNSCentralManagerDelegate - -- (void)centralManager:(GNSCentralManager *)centralManager - didDiscoverPeer:(GNSCentralPeerManager *)centralPeerManager - advertisementData:(nullable NSDictionary *)advertisementData { - if (!_callbacksEnabled) return; - - // Retrieve the cached peripheral info. - NSUUID *peerId = centralPeerManager.identifier; - GNCMPeripheralInfo *peripheralInfo = _nearbyPeripheralsByID[peerId]; - if (!peripheralInfo) return; - - __weak __typeof__(self) weakSelf = self; - [self callbackAsync:^{ - _requestConnectionHandler(^(NSString *serviceID, GNCMConnectionHandler connectionHandler) { - __strong __typeof__(self) strongSelf = weakSelf; - if (!strongSelf) return; - - void (^callConnectionHandler)(GNSSocket *__nullable) = ^(GNSSocket *__nullable socket) { - __strong __typeof__(self) strongSelf = weakSelf; - if (!strongSelf->_callbacksEnabled) return; - [strongSelf callbackAsync:^{ - if (!socket) { - NSLog(@"[NEARBY] Central failed to create BLE socket"); - connectionHandler(nil); - } else { - GNCMBleConnection *connection = - [GNCMBleConnection connectionWithSocket:socket - serviceID:serviceID - expectedIntroPacket:NO - callbackQueue:strongSelf->_clientCallbackQueue]; - connection.connectionHandlers = connectionHandler(connection); - } - }]; - }; - - dispatch_async(strongSelf->_selfQueue, ^{ - // A connection is being requested, so establish a BLE socket. Make sure to use the most - // up-to-date GNSCentralPeerManager in case the MAC address rotated. The cached - // peripheral info should be the single source of truth for which MAC address to use. - GNSCentralPeerManager *updatedCentralPeerManager = - [centralManager retrieveCentralPeerWithIdentifier:peripheralInfo.peripheral.identifier]; - if (!updatedCentralPeerManager) { - callConnectionHandler(nil); - } - - // Make a socket connection. - [updatedCentralPeerManager - socketWithPairingCharacteristic:NO - completion:^(GNSSocket *socket, NSError *error) { - __strong __typeof__(self) strongSelf = weakSelf; - if (!strongSelf) return; - dispatch_async(strongSelf->_selfQueue, ^{ - if (!error) { - // Call the connection handler when the socket has - // connected or fails to connect. - GNCMWaitForConnection(socket, ^(BOOL didConnect) { - callConnectionHandler(didConnect ? socket : nil); - }); - } else { - callConnectionHandler(nil); - } - }); - }]; - }); - }); - }]; -} - -- (void)centralManagerDidUpdateBleState:(GNSCentralManager *)centralManager { - // No op. -} - -#pragma mark Private - -/** This method assumes it's being called on selfQueue. */ -- (void)completeGATTReadWithData:(NSData *)advertisement - forPeripheralInfo:(GNCMPeripheralInfo *)peripheralInfo { - NSAssert(peripheralInfo, @"Nil peripheralInfo"); - if (peripheralInfo) { - CBPeripheral *peripheral = peripheralInfo.peripheral; - NSUUID *peripheralID = peripheral.identifier; - // Store the data for the socket callback, and report the peripheral to the socket library. - [_socketCentralManager retrievePeripheralWithIdentifier:peripheralID - advertisementData:peripheralInfo.advertisementData]; - } -} - -/** Signals the central manager to start scanning. Must be called on _selfQueue. */ -- (void)startScanningInternal { - if (![_centralManager isScanning]) { - NSLog(@"[NEARBY] startScanningInternal"); - - [_centralManager - scanForPeripheralsWithServices:@[ _serviceUUID ] - options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES}]; - } -} - -/** Signals the central manager to stop scanning. Must be called on _selfQueue */ -- (void)stopScanningInternal { - if ([_centralManager isScanning]) { - NSLog(@"[NEARBY] stopScanningInternal"); - _centralState = GNCMCentralStateStopped; - - [_centralManager stopScan]; - } -} - -/** Calls the specified block on the callback queue. */ -- (void)callbackAsync:(dispatch_block_t)block { - dispatch_queue_t clientCallbackQueue = _clientCallbackQueue; // don't capture |self| - dispatch_async(_internalCallbackQueue, ^{ - dispatch_sync(clientCallbackQueue, block); - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h b/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h deleted file mode 100644 index 8511da36..00000000 --- a/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h +++ /dev/null @@ -1,79 +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. - -#import - -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" - -@class CBCharacteristic; -@class CBUUID; - -NS_ASSUME_NONNULL_BEGIN - -/** - * GNCMBlePeripheral advertises the specified service UUID via Ble for the purpose of being - * discovered by a central using the GNCMBleCentral class. - * - * This class is thread-safe. Any calls made to it (and to the objects/closures it passes back via - * callbacks) can be made from any thread/queue. Callbacks made from this class are called on the - * specified queue. - */ -@interface GNCMBlePeripheral : NSObject - -- (instancetype)init NS_DESIGNATED_INITIALIZER; - -/** - * Adds GATT CBService. - * - * @param serviceUUID A GATT service ID to advertise for. - */ -- (void)addCBServiceWithUUID:(CBUUID *)serviceUUID; - -/** - * Adds GATT CBCharacteristic. - * - * @param characteristic A characteristic CBUUID. - */ -- (void)addCharacteristic:(CBCharacteristic *)characteristic; - -/** - * Updates GATT CBCharacteristic with value. - * - * @param value The NSData to advertise. - * @param characteristicUUID A characteristic CBUUID. - */ -- (void)updateValue:(NSData *)value forCharacteristic:(CBUUID *)characteristicUUID; - -/** - * Stops GATT server service. - */ -- (void)stopGATTService; - -/** - * Starts advertising with service UUID and advertisement data. - * - * @param serviceUUID A string that uniquely identifies the advertised service to search for. - * @param advertisementData The data to advertise. - * @param endpointconnectedHandler The handler that is called when a discoverer connects. - * @param callbackQueue The queue on which all callbacks are made. If |callbackQueue| is not - * provided, then the main queue is used in the function. - */ -- (BOOL)startAdvertisingWithServiceUUID:(NSString *)serviceUUID - advertisementData:(NSData *)advertisementData - endpointConnectedHandler:(GNCMConnectionHandler)endpointConnectedHandler - callbackQueue:(nullable dispatch_queue_t)callbackQueue; - -@end - -NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.m b/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.m deleted file mode 100644 index d915a3c3..00000000 --- a/internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.m +++ /dev/null @@ -1,293 +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. - -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h" - -#import - -#import "internal/platform/implementation/apple/GNCUtils.h" -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleConnection.h" -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleUtils.h" -#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/GNCMConnection.h" - -NS_ASSUME_NONNULL_BEGIN - -typedef NS_ENUM(NSUInteger, GNCMPeripheralState) { - GNCMPeripheralStateStopped, - GNCMPeripheralStateAdvertising, -}; - -@interface GNCMBlePeripheral () -@end - -@implementation GNCMBlePeripheral { - /** Service UUID for advertisement. */ - CBMutableService *_advertisementService; - /** GATT service for GATT connection. */ - CBMutableService *_GATTService; - /** GATT characteristics for GATT connection. */ - NSMutableArray *_gattCharacteristics; - /** CBUUID characteristic to NSData value dictionary for GATT connection. */ - NSMutableDictionary *_gattCharacteristicValues; - /** Data to be advertised. */ - NSData *_advertisementData; - /** Peripheral manager used to advertise or connect to peripherals. */ - CBPeripheralManager *_peripheralManager; - /** Serial background queue for |peripheralManager|. */ - dispatch_queue_t _selfQueue; - /** Peripheral state for stop or advertising. */ - GNCMPeripheralState _state; - /** Peripheral manager used for socket connection based on weave protocol. */ - GNSPeripheralManager *_socketPeripheralManager; - /** Peripheral service manager used to manage one BLE service. */ - GNSPeripheralServiceManager *_socketPeripheralServiceManager; - /** Client callback queue. If client doesn't assign it, then use main queue. */ - dispatch_queue_t _clientCallbackQueue; - /** Internal async priority queue. */ - dispatch_queue_t _internalCallbackQueue; - /** Flag to disable callback for dealloc. */ - BOOL _callbacksEnabled; -} - -- (instancetype)init { - if (self = [super init]) { - // To make this class thread-safe, use a serial queue for all state changes, and have Core - // Bluetooth also use this queue. - _selfQueue = dispatch_queue_create("GNCPeripheralManagerQueue", DISPATCH_QUEUE_SERIAL); - - _state = GNCMPeripheralStateStopped; - } - return self; -} - -- (void)dealloc { - // These calls must be made on |selfQueue|. Can't capture |self| in an async block, so must use - // dispatch_sync. This means delloc must be called from an external queue, which means |self| - // must never be captured by any escaping block used in this class. - dispatch_sync(_selfQueue, ^{ - [self stopAdvertisingInternal]; - - _callbacksEnabled = NO; - }); -} - -- (void)addCBServiceWithUUID:(CBUUID *)serviceUUID { - if (!_GATTService) { - // If it has been called, then don't do it again. Initialize one time. - _GATTService = [[CBMutableService alloc] initWithType:serviceUUID primary:YES]; - _gattCharacteristics = [[NSMutableArray alloc] init]; - _gattCharacteristicValues = [[NSMutableDictionary alloc] init]; - } -} - -- (void)addCharacteristic:(CBCharacteristic *)characteristic { - if (_gattCharacteristics) { - [_gattCharacteristics addObject:characteristic]; - } - if (_gattCharacteristicValues) { - [_gattCharacteristicValues setObject:[[NSData alloc] init] forKey:characteristic.UUID]; - } -} - -- (void)updateValue:(NSData *)value forCharacteristic:(CBUUID *)characteristicUUID { - if ([_gattCharacteristicValues objectForKey:characteristicUUID]) { - [_gattCharacteristicValues setObject:value forKey:characteristicUUID]; - } -} - -- (void)stopGATTService { - if (!_GATTService) return; - dispatch_sync(_selfQueue, ^{ - [_peripheralManager removeService:_GATTService]; - }); -} - -- (BOOL)startAdvertisingWithServiceUUID:(NSString *)serviceUUID - advertisementData:(NSData *)advertisementData - endpointConnectedHandler:(GNCMConnectionHandler)endpointConnectedHandler - callbackQueue:(nullable dispatch_queue_t)callbackQueue { - NSLog(@"[NEARBY] Client rquests startAdvertising"); - // The client may be using the callback queue for other purposes, so wrap it with a private - // queue to know with certainty when all callbacks are done. - _clientCallbackQueue = callbackQueue ?: dispatch_get_main_queue(); - _internalCallbackQueue = - dispatch_queue_create("GNCMBlePeripheralCallbackQueue", DISPATCH_QUEUE_PRIORITY_DEFAULT); - _callbacksEnabled = YES; - - _advertisementService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:serviceUUID] - primary:YES]; - _advertisementData = [advertisementData copy]; - - // To make this class thread-safe, use a serial queue for all state changes, and have Core - // Bluetooth also use this queue. - _selfQueue = dispatch_queue_create("GNCPeripheralManagerQueue", DISPATCH_QUEUE_SERIAL); - __weak __typeof__(self) weakSelf = self; - // Set up the peripheral manager for the socket. This must be done before creating the - // peripheral manager for the advertisement data because it's started/stopped in the - // -peripheralManagerDidUpdateState: callback. - _socketPeripheralServiceManager = [[GNSPeripheralServiceManager alloc] - initWithBleServiceUUID:_advertisementService.UUID - addPairingCharacteristic:NO - shouldAcceptSocketHandler:^BOOL(GNSSocket *socket) { - // Call the connection handler when the socket has connected or fails to connect. - GNCMWaitForConnection(socket, ^(BOOL didConnect) { - [weakSelf establishConnectionWithSocket:socket - didConnect:didConnect - endpointConnectedHandler:endpointConnectedHandler]; - }); - return YES; - } - queue:_selfQueue]; - _socketPeripheralManager = [[GNSPeripheralManager alloc] initWithAdvertisedName:nil - restoreIdentifier:nil - queue:_selfQueue]; - [_socketPeripheralManager addPeripheralServiceManager:_socketPeripheralServiceManager - bleServiceAddedCompletion:^(NSError *error) { - NSLog(@"Failed to add service"); - }]; - - // Set up the peripheral manager for the advertisement data. - _peripheralManager = [[CBPeripheralManager alloc] - initWithDelegate:self - queue:_selfQueue - options:@{CBPeripheralManagerOptionShowPowerAlertKey : @NO}]; - - if (_GATTService) { - if (_gattCharacteristics && _gattCharacteristics.count > 0) { - _GATTService.characteristics = _gattCharacteristics; - } - } - _state = GNCMPeripheralStateAdvertising; - return YES; -} - -#pragma mark CBPeripheralManagerDelegate - -- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral { - NSLog(@"[NEARBY] peripheralManagerDidUpdateState %li", (long)peripheral.state); - if (peripheral.state == CBManagerStatePoweredOn && !peripheral.isAdvertising && - _state == GNCMPeripheralStateAdvertising) { - NSLog(@"[NEARBY] CBPeripheralManager powered on; starting advertising"); - [_socketPeripheralManager start]; - [self startAdvertisingInternal]; - } else { - NSLog(@"[NEARBY] CBPeripheralManager not powered on; stopping advertising"); - [self stopAdvertisingInternal]; - } -} - -- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral - error:(nullable NSError *)error { - if (error) { - NSLog(@"[NEARBY] Error starting advertising: %@,", [error localizedDescription]); - return; - } - if (_peripheralManager.state != CBManagerStatePoweredOn) { - NSLog(@"[NEARBY] Error starting advertising: peripheral manager not on!"); - return; - } - - NSLog(@"[NEARBY] Peripheral manager started advertising"); -} - -- (void)peripheralManager:(CBPeripheralManager *)peripheral - didReceiveReadRequest:(CBATTRequest *)request { - NSLog(@"[NEARBY] peripheralManager:didReceiveReadRequest"); - // This is called when a central asks to read a characteristic's value. - CBATTError error = CBATTErrorAttributeNotFound; - NSData *value = _gattCharacteristicValues[request.characteristic.UUID]; - if (value != nil && value.length > 0) { - if (request.offset > value.length) { - error = CBATTErrorInvalidOffset; - } else { - // Reply with the advertisement data. - NSRange rangeFromOffset = NSMakeRange(request.offset, value.length - request.offset); - request.value = [value subdataWithRange:rangeFromOffset]; - error = CBATTErrorSuccess; - } - } - [_peripheralManager respondToRequest:request withResult:error]; -} - -#pragma mark Private - -/** Signals the peripheral manager to start advertising. Must be called on _selfQueue */ -- (void)startAdvertisingInternal { - if (![_peripheralManager isAdvertising]) { - NSLog(@"[NEARBY] startAdvertisingInternal"); - - if (_GATTService) { - [_peripheralManager addService:_GATTService]; - } - [_peripheralManager startAdvertising:@{ - CBAdvertisementDataServiceUUIDsKey : @[ _advertisementService.UUID ], - CBAdvertisementDataLocalNameKey : _advertisementData - }]; - } -} - -/** Signals the peripheral manager to stop advertising. Must be called on _selfQueue */ -- (void)stopAdvertisingInternal { - if ([_peripheralManager isAdvertising]) { - NSLog(@"[NEARBY] stopAdvertisingInternal"); - _state = GNCMPeripheralStateStopped; - - if (_GATTService) { - [_peripheralManager removeService:_GATTService]; - } - [_peripheralManager stopAdvertising]; - } -} - -/** - * Connects with socket and callback the |GNCMBleConnection| is established or nil if it is not. - */ -- (void)establishConnectionWithSocket:(GNSSocket *)socket - didConnect:(BOOL)didConnect - endpointConnectedHandler:(GNCMConnectionHandler)endpointConnectedHandler { - if (!_callbacksEnabled) { - return; - } - - [self callbackAsync:^{ - if (!didConnect) { - NSLog(@"[NEARBY] Peripheral failed to create BLE socket"); - endpointConnectedHandler(nil); - } else { - GNCMBleConnection *connection = [GNCMBleConnection connectionWithSocket:socket - serviceID:nil - expectedIntroPacket:YES - callbackQueue:_clientCallbackQueue]; - connection.connectionHandlers = endpointConnectedHandler(connection); - } - }]; -} - -/** - * Calls the specified block on the callback queue, preventing it from being dispatched to the - * client callback queue when callbacks are disabled. And without capturing |self|, since - * callbacks are disabled in dealloc. - */ -- (void)callbackAsync:(dispatch_block_t)block { - dispatch_queue_t clientCallbackQueue = _clientCallbackQueue; // don't capture |self| - dispatch_async(_internalCallbackQueue, ^{ - dispatch_sync(clientCallbackQueue, block); - }); -} - -@end - -NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Tests/BUILD b/internal/platform/implementation/apple/Tests/BUILD index 9cc10150..d10e8cc3 100644 --- a/internal/platform/implementation/apple/Tests/BUILD +++ b/internal/platform/implementation/apple/Tests/BUILD @@ -31,8 +31,6 @@ objc_library( "GNCBLEMedium+Testing.h", "GNCBLEMediumTest.m", "GNCBLEUtilsTest.mm", - "GNCBleTest.mm", - "GNCBluetoothAdapterTest.mm", "GNCCryptoTest.mm", "GNCFakeCentralManager.h", "GNCFakeCentralManager.m", @@ -53,8 +51,8 @@ objc_library( "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", "//internal/platform/implementation:types", - "//internal/platform/implementation/apple", - "//internal/platform/implementation/apple:ble", + "//internal/platform/implementation/apple", # buildcleaner: keep + "//internal/platform/implementation/apple:ble_v2", "//internal/platform/implementation/apple/Mediums", "//third_party/apple_frameworks:CoreBluetooth", "//third_party/apple_frameworks:Foundation", diff --git a/internal/platform/implementation/apple/Tests/GNCBleTest.mm b/internal/platform/implementation/apple/Tests/GNCBleTest.mm deleted file mode 100644 index d8679575..00000000 --- a/internal/platform/implementation/apple/Tests/GNCBleTest.mm +++ /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. - -#import -#include "internal/platform/implementation/apple/bluetooth_adapter.h" - -#include -#include -#include - -#include "internal/platform/byte_array.h" -#include "internal/platform/implementation/ble_v2.h" -#include "internal/platform/implementation/bluetooth_adapter.h" -#include "internal/platform/implementation/platform.h" - -using ::nearby::ByteArray; -using ::nearby::Uuid; -using ::nearby::api::BluetoothAdapter; -using ::nearby::api::ImplementationPlatform; -using ::nearby::api::ble_v2::BleAdvertisementData; -using ::nearby::api::ble_v2::BleMedium; -using ::nearby::api::ble_v2::GattCharacteristic; -using ::nearby::api::ble_v2::TxPowerLevel; -using IOSBluetoothAdapter = ::nearby::apple::BluetoothAdapter; - -static const char *const kAdvertisementString = "\x0a\x0b\x0c\x0d"; -static const TxPowerLevel kTxPowerLevel = TxPowerLevel::kHigh; - -@interface GNCBleTest : XCTestCase -@end - -// TODO(b/222392304): More tests on GNCBleTest. -@implementation GNCBleTest { - std::unique_ptr _adapter; - std::unique_ptr _ble; -} - -- (void)setUp { - [super setUp]; - _adapter = ImplementationPlatform::CreateBluetoothAdapter(); - _ble = ImplementationPlatform::CreateBleV2Medium(*_adapter); -} - -- (void)testStartandStopAdvertising { - Uuid service_uuid(1234, 5678); - ByteArray advertisement_bytes{std::string(kAdvertisementString)}; - - // Assemble regular advertisement data. - BleAdvertisementData advertising_data; - advertising_data.is_extended_advertisement = false; - advertising_data.service_data = {{service_uuid, advertisement_bytes}}; - - XCTAssertTrue(_ble->StartAdvertising(advertising_data, - {.tx_power_level = kTxPowerLevel, .is_connectable = true})); - - [NSThread sleepForTimeInterval:0.1]; - - XCTAssertTrue(_ble->StopAdvertising()); -} - -- (void)testStartandStopScanning { - Uuid service_uuid(1234, 5678); - - XCTAssertTrue(_ble->StartScanning(service_uuid, kTxPowerLevel, BleMedium::ScanCallback{})); - - [NSThread sleepForTimeInterval:0.1]; - - XCTAssertTrue(_ble->StopScanning()); -} - -- (void)testGattServerWorking { - // Test creating gatt_server. - auto gatt_server = _ble->StartGattServer(/*ServerGattConnectionCallback=*/{}); - XCTAssert(gatt_server != nullptr); - - // Test creating characteristic. - Uuid service_uuid(1234, 5678); - Uuid characteristic_uuid(5678, 1234); - GattCharacteristic::Permission permission = GattCharacteristic::Permission::kRead; - GattCharacteristic::Property property = GattCharacteristic::Property::kRead; - - // NOLINTNEXTLINE - absl::optional gatt_characteristic = - gatt_server->CreateCharacteristic(service_uuid, characteristic_uuid, permission, property); - XCTAssertTrue(gatt_characteristic.has_value()); - - // Test updating characteristic. - ByteArray any_byte("any"); - XCTAssertTrue(gatt_server->UpdateCharacteristic(gatt_characteristic.value(), any_byte)); - - gatt_server->Stop(); -} - -- (void)testCreateGattClient { - IOSBluetoothAdapter *adapter = static_cast(_adapter.get()); - auto gatt_client = _ble->ConnectToGattServer(adapter->GetPeripheral(), kTxPowerLevel, {}); - - XCTAssert(gatt_client != nullptr); -} - -@end diff --git a/internal/platform/implementation/apple/Tests/GNCBluetoothAdapterTest.mm b/internal/platform/implementation/apple/Tests/GNCBluetoothAdapterTest.mm deleted file mode 100644 index bb4955c8..00000000 --- a/internal/platform/implementation/apple/Tests/GNCBluetoothAdapterTest.mm +++ /dev/null @@ -1,87 +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. - -#import - -#include - -#include "internal/platform/implementation/apple/bluetooth_adapter.h" -#include "internal/platform/implementation/ble_v2.h" -#include "internal/platform/implementation/bluetooth_adapter.h" - -using ::nearby::apple::BlePeripheral; -using ::nearby::apple::BluetoothAdapter; -using ScanMode = ::nearby::api::BluetoothAdapter::ScanMode; -using Status = ::nearby::api::BluetoothAdapter::Status; - -static const char kAdapterName[] = "MyBtAdapter"; -static const char kMacAddress[] = "4C:8B:1D:CE:BA:D1"; - -@interface GNCBluetoothAdapterTest : XCTestCase -@end - -@implementation GNCBluetoothAdapterTest - -- (void)testName { - BluetoothAdapter adapter; - - XCTAssertTrue(adapter.SetName(kAdapterName)); - - XCTAssertEqual(adapter.GetName(), std::string(kAdapterName)); -} - -- (void)testGetScanMode { - BluetoothAdapter adapter; - - // Always return kNone as ScanMode is not supported . - XCTAssertEqual(adapter.GetScanMode(), ScanMode::kNone); -} - -- (void)testSetScanMode_DefaultUnsupported { - BluetoothAdapter adapter; - - XCTAssertFalse(adapter.SetScanMode(ScanMode::kNone)); - XCTAssertFalse(adapter.SetScanMode(ScanMode::kConnectable)); - XCTAssertFalse(adapter.SetScanMode(ScanMode::kConnectableDiscoverable)); -} - -- (void)testSetStatus { - BluetoothAdapter adapter; - - XCTAssertTrue(adapter.SetStatus(Status::kDisabled)); - XCTAssertFalse(adapter.IsEnabled()); - - XCTAssertTrue(adapter.SetStatus(Status::kEnabled)); - XCTAssertTrue(adapter.IsEnabled()); -} - -- (void)testMacAddress { - BluetoothAdapter adapter; - - adapter.SetMacAddress(kMacAddress); - - XCTAssertEqual(adapter.GetMacAddress(), std::string(kMacAddress)); -} - -- (void)testGetPeripheral { - BluetoothAdapter adapter; - - adapter.SetMacAddress(kMacAddress); - - // The peripheral from adapter, the MAC address is the same. - BlePeripheral& peripheral = adapter.GetPeripheral(); - XCTAssertEqual(adapter.GetMacAddress(), peripheral.GetAddress()); -} - -@end diff --git a/internal/platform/implementation/apple/ble.h b/internal/platform/implementation/apple/ble.h index 6d41a16d..fd540fe7 100644 --- a/internal/platform/implementation/apple/ble.h +++ b/internal/platform/implementation/apple/ble.h @@ -12,218 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLE_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLE_H_ +// Note: File language is detected using heuristics. Many Objective-C++ headers are incorrectly +// classified as C++ resulting in invalid linter errors. The use of "NSArray" and other Foundation +// classes like "NSData", "NSDictionary" and "NSUUID" are highly weighted for Objective-C and +// Objective-C++ scores. Oddly, "#import " does not contribute any points. +// This comment alone should be enough to trick the IDE in to believing this is actually some sort +// of Objective-C file. See: cs/google3/devtools/search/lang/recognize_language_classifiers_data -#import #import -#include -#include -#include - -#include "absl/types/optional.h" -#include "internal/platform/cancellation_flag.h" -#include "internal/platform/implementation/apple/bluetooth_adapter.h" -#include "internal/platform/implementation/ble_v2.h" -#include "internal/platform/implementation/bluetooth_adapter.h" - -#import "internal/platform/implementation/apple/Mediums/GNCMConnection.h" - -@class GNCMBlePeripheral, GNCMBleCentral; - -namespace nearby { -namespace apple { - -/** InputStream that reads from GNCMConnection. */ -class BleInputStream : public InputStream { - public: - BleInputStream(); - ~BleInputStream() override; - - ExceptionOr Read(std::int64_t size) override; - Exception Close() override; - - GNCMConnectionHandlers *GetConnectionHandlers() { return connectionHandlers_; } - - private: - GNCMConnectionHandlers *connectionHandlers_; - NSMutableArray *newDataPackets_; - NSMutableData *accumulatedData_; - NSCondition *condition_; -}; - -/** OutputStream that writes to GNCMConnection. */ -class BleOutputStream : public OutputStream { - public: - explicit BleOutputStream(id connection) - : connection_(connection), condition_([[NSCondition alloc] init]) {} - ~BleOutputStream() override; - - Exception Write(const ByteArray &data) override; - Exception Flush() override; - Exception Close() override; - - private: - id connection_; - NSCondition *condition_; -}; - -/** Concrete BleSocket implementation. */ -class BleSocket : public api::ble_v2::BleSocket { - public: - BleSocket(id connection, BlePeripheral *peripheral); - ~BleSocket() override; - - InputStream &GetInputStream() override { return *input_stream_; } - OutputStream &GetOutputStream() override { return *output_stream_; } - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - BlePeripheral *GetRemotePeripheral() override { return peripheral_; } - - bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); - - private: - void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - mutable absl::Mutex mutex_; - bool closed_ ABSL_GUARDED_BY(mutex_) = false; - std::unique_ptr input_stream_; - std::unique_ptr output_stream_; - BlePeripheral *peripheral_; -}; - -/** Concrete BleServerSocket implementation. */ -class BleServerSocket : public api::ble_v2::BleServerSocket { - public: - ~BleServerSocket() override; - - std::unique_ptr Accept() override ABSL_LOCKS_EXCLUDED(mutex_); - Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); - - bool Connect(std::unique_ptr socket) ABSL_LOCKS_EXCLUDED(mutex_); - void SetCloseNotifier(absl::AnyInvocable notifier) ABSL_LOCKS_EXCLUDED(mutex_); - - private: - Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - - mutable absl::Mutex mutex_; - absl::CondVar cond_; - absl::flat_hash_set> pending_sockets_ ABSL_GUARDED_BY(mutex_); - absl::AnyInvocable close_notifier_ ABSL_GUARDED_BY(mutex_); - bool closed_ ABSL_GUARDED_BY(mutex_) = false; -}; - -/** Concrete BleMedium implementation. */ -class BleMedium : public api::ble_v2::BleMedium { - public: - explicit BleMedium(api::BluetoothAdapter &adapter); - - // api::BleMedium: - bool StartAdvertising(const api::ble_v2::BleAdvertisementData &advertising_data, - api::ble_v2::AdvertiseParameters advertise_set_parameters) override; - bool StopAdvertising() override; - std::unique_ptr StartAdvertising( - const api::ble_v2::BleAdvertisementData &advertising_data, - api::ble_v2::AdvertiseParameters advertise_parameters, AdvertisingCallback callback) override; - - bool StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, - api::ble_v2::BleMedium::ScanCallback scan_callback) override; - bool StopScanning() override; - std::unique_ptr StartScanning(const Uuid &service_uuid, - api::ble_v2::TxPowerLevel tx_power_level, - ScanningCallback callback) override; - std::unique_ptr StartGattServer( - api::ble_v2::ServerGattConnectionCallback callback) override; - std::unique_ptr ConnectToGattServer( - api::ble_v2::BlePeripheral &peripheral, api::ble_v2::TxPowerLevel tx_power_level, - api::ble_v2::ClientGattConnectionCallback callback) override; - std::unique_ptr OpenServerSocket( - const std::string &service_id) override; - std::unique_ptr Connect(const std::string &service_id, - api::ble_v2::TxPowerLevel tx_power_level, - api::ble_v2::BlePeripheral &peripheral, - CancellationFlag *cancellation_flag) override; - bool IsExtendedAdvertisementsAvailable() override; - - bool GetRemotePeripheral(const std::string& mac_address, - GetRemotePeripheralCallback callback) override { - return false; - } - - bool GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId id, - GetRemotePeripheralCallback callback) override { - return false; - } - - private: - // A concrete implemenation for GattServer. - class GattServer : public api::ble_v2::GattServer { - public: - GattServer() = default; - GattServer(BluetoothAdapter *adapter, GNCMBlePeripheral *peripheral) - : adapter_(adapter), peripheral_(peripheral) {} - - absl::optional CreateCharacteristic( - const Uuid &service_uuid, const Uuid &characteristic_uuid, - api::ble_v2::GattCharacteristic::Permission permission, - api::ble_v2::GattCharacteristic::Property property) override; - - bool UpdateCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, - const nearby::ByteArray &value) override; - absl::Status NotifyCharacteristicChanged(const api::ble_v2::GattCharacteristic &characteristic, - bool confirm, const ByteArray &new_value) override; - void Stop() override; - BlePeripheral &GetBlePeripheral() override { return adapter_->GetPeripheral(); } - - private: - BluetoothAdapter *adapter_ = nullptr; - GNCMBlePeripheral *peripheral_; - }; - - // A concrete implemenation for GattClient. - class GattClient : public api::ble_v2::GattClient { - public: - GattClient() = default; - explicit GattClient(GNCMBleCentral *central, const std::string &peripheral_id) - : central_(central), peripheral_id_(peripheral_id) {} - - bool DiscoverServiceAndCharacteristics(const Uuid &service_uuid, - const std::vector &characteristic_uuids) override; - - // NOLINTNEXTLINE - absl::optional GetCharacteristic( - const Uuid &service_uuid, const Uuid &characteristic_uuid) override; - - // NOLINTNEXTLINE - absl::optional ReadCharacteristic( - const api::ble_v2::GattCharacteristic &characteristic) override; - - bool WriteCharacteristic(const api::ble_v2::GattCharacteristic &characteristic, - absl::string_view value, - api::ble_v2::GattClient::WriteType write_type) override; - - bool SetCharacteristicSubscription( - const api::ble_v2::GattCharacteristic &characteristic, bool enable, - absl::AnyInvocable on_characteristic_changed_cb) override; - - void Disconnect() override; - - private: - GNCMBleCentral *central_; - std::string peripheral_id_; - absl::flat_hash_map - gatt_characteristic_values_; - }; - - absl::Mutex mutex_; - BluetoothAdapter *adapter_; - GNCMBlePeripheral *peripheral_; - GNCMBleCentral *central_; - absl::flat_hash_map server_sockets_ ABSL_GUARDED_BY(mutex_); - dispatch_queue_t callback_queue_; -}; - -} // namespace apple -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLE_H_ +#import "internal/platform/implementation/apple/ble_medium.h" diff --git a/internal/platform/implementation/apple/ble.mm b/internal/platform/implementation/apple/ble.mm deleted file mode 100644 index a6fdfd77..00000000 --- a/internal/platform/implementation/apple/ble.mm +++ /dev/null @@ -1,600 +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. - -#import "internal/platform/implementation/apple/ble.h" - -#include - -#include -#include -#include -#include - -#include "internal/platform/implementation/apple/bluetooth_adapter.h" -#include "internal/platform/implementation/apple/utils.h" -#include "internal/platform/implementation/ble_v2.h" - -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h" -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBlePeripheral.h" - -namespace nearby { -namespace apple { - -using Permission = api::ble_v2::GattCharacteristic::Permission; -using Property = api::ble_v2::GattCharacteristic::Property; - -namespace { - -CBAttributePermissions PermissionToCBPermissions(Permission permission) { - CBAttributePermissions characteristPermissions = 0; - if ((permission & Permission::kRead) != Permission::kNone) { - characteristPermissions |= CBAttributePermissionsReadable; - } - if ((permission & Permission::kWrite) != Permission::kNone) { - characteristPermissions |= CBAttributePermissionsWriteable; - } - return characteristPermissions; -} - -CBCharacteristicProperties PropertiesToCBProperties(Property property) { - CBCharacteristicProperties characteristicProperties = 0; - if ((property & Property::kRead) != Property::kNone) { - characteristicProperties |= CBCharacteristicPropertyRead; - } - if ((property & Property::kWrite) != Property::kNone) { - characteristicProperties |= CBCharacteristicPropertyWrite; - } - if ((property & Property::kIndicate) != Property::kNone) { - characteristicProperties |= CBCharacteristicPropertyIndicate; - } - if ((property & Property::kNotify) != Property::kNone) { - characteristicProperties |= CBCharacteristicPropertyNotify; - } - return characteristicProperties; -} - -} // namespace - -using ::nearby::api::ble_v2::BleAdvertisementData; -using ::nearby::api::ble_v2::TxPowerLevel; -using ScanCallback = ::nearby::api::ble_v2::BleMedium::ScanCallback; - -/** InputStream that reads from GNCMConnection. */ -BleInputStream::BleInputStream() - : newDataPackets_([NSMutableArray array]), - accumulatedData_([NSMutableData data]), - condition_([[NSCondition alloc] init]) { - // Create the handlers of incoming data from the remote endpoint. - connectionHandlers_ = [GNCMConnectionHandlers - payloadHandler:^(NSData* data) { - [condition_ lock]; - // Add the incoming data to the data packet array to be processed in read() below. - [newDataPackets_ addObject:data]; - [condition_ signal]; - [condition_ unlock]; - } - disconnectedHandler:^{ - [condition_ lock]; - // Release the data packet array, meaning the stream has been closed or severed. - newDataPackets_ = nil; - [condition_ signal]; - [condition_ unlock]; - }]; -} - -BleInputStream::~BleInputStream() { - NSCAssert(!newDataPackets_, @"BleInputStream not closed before destruction"); -} - -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 (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]; - } - - 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) { - NSLog(@"[NEARBY] Input stream: Received data of size: %lu", (unsigned long)dataToReturn.length); - return ExceptionOr(ByteArrayFromNSData(dataToReturn)); - } else { - return ExceptionOr{Exception::kIo}; - } -} - -Exception BleInputStream::Close() { - // Unblock pending read operation. - [condition_ lock]; - newDataPackets_ = nil; - [condition_ signal]; - [condition_ unlock]; - return {Exception::kSuccess}; -} - -/** OutputStream that writes to GNCMConnection. */ -BleOutputStream::~BleOutputStream() { - NSCAssert(!connection_, @"BleOutputStream not closed before destruction"); -} - -Exception BleOutputStream::Write(const ByteArray& data) { - [condition_ lock]; - NSLog(@"[NEARBY] Sending data of size: %lu", (unsigned long)NSDataFromByteArray(data).length); - - NSMutableData* packet = [NSMutableData dataWithData:NSDataFromByteArray(data)]; - - // Send the data, blocking until the completion handler is called. - __block GNCMPayloadResult sendResult = GNCMPayloadResultFailure; - __block bool isComplete = NO; - NSCondition* condition = condition_; // don't capture |this| in completion - - // Check if connection_ is nil, then just don't wait and return as failure. - if (connection_ != nil) { - [connection_ sendData:packet - progressHandler:^(size_t count) { - } - completion:^(GNCMPayloadResult result) { - // Make sure we haven't already reported completion before. This prevents a crash - // where we try leaving a dispatch group more times than we entered it. - // b/79095653. - if (isComplete) { - return; - } - isComplete = YES; - sendResult = result; - [condition lock]; - [condition signal]; - [condition unlock]; - }]; - [condition_ wait]; - [condition_ unlock]; - } else { - sendResult = GNCMPayloadResultFailure; - [condition_ unlock]; - } - - if (sendResult == GNCMPayloadResultSuccess) { - return {Exception::kSuccess}; - } else { - return {Exception::kIo}; - } -} - -Exception BleOutputStream::Flush() { - // The write() function blocks until the data is received by the remote endpoint, so there's - // nothing to do here. - return {Exception::kSuccess}; -} - -Exception BleOutputStream::Close() { - // Unblock pending write operation. - [condition_ lock]; - connection_ = nil; - [condition_ signal]; - [condition_ unlock]; - return {Exception::kSuccess}; -} - -/** BleSocket implementation.*/ -BleSocket::BleSocket(id connection, BlePeripheral* peripheral) - : input_stream_(new BleInputStream()), - output_stream_(new BleOutputStream(connection)), - peripheral_(peripheral) {} - -BleSocket::~BleSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -bool BleSocket::IsClosed() const { - absl::MutexLock lock(&mutex_); - return closed_; -} - -Exception BleSocket::Close() { - absl::MutexLock lock(&mutex_); - DoClose(); - return {Exception::kSuccess}; -} - -void BleSocket::DoClose() { - if (!closed_) { - input_stream_->Close(); - output_stream_->Close(); - closed_ = true; - } -} - -/** WifiLanServerSocket implementation. */ -BleServerSocket::~BleServerSocket() { - absl::MutexLock lock(&mutex_); - DoClose(); -} - -std::unique_ptr BleServerSocket::Accept() { - absl::MutexLock lock(&mutex_); - while (!closed_ && pending_sockets_.empty()) { - cond_.Wait(&mutex_); - } - // Return early if closed. - if (closed_) return {}; - - auto remote_socket = std::move(pending_sockets_.extract(pending_sockets_.begin()).value()); - return std::move(remote_socket); -} - -bool BleServerSocket::Connect(std::unique_ptr socket) { - absl::MutexLock lock(&mutex_); - if (closed_) { - return false; - } - // add client socket to the pending list - pending_sockets_.insert(std::move(socket)); - cond_.SignalAll(); - if (closed_) { - return false; - } - return true; -} - -void BleServerSocket::SetCloseNotifier(absl::AnyInvocable notifier) { - absl::MutexLock lock(&mutex_); - close_notifier_ = std::move(notifier); -} - -Exception BleServerSocket::Close() { - absl::MutexLock lock(&mutex_); - return DoClose(); -} - -Exception BleServerSocket::DoClose() { - bool should_notify = !closed_; - closed_ = true; - if (should_notify) { - cond_.SignalAll(); - if (close_notifier_) { - auto notifier = std::move(close_notifier_); - mutex_.Unlock(); - // Notifier may contain calls to public API, and may cause deadlock, if - // mutex_ is held during the call. - notifier(); - mutex_.Lock(); - } - } - return {Exception::kSuccess}; -} - -/** BleMedium implementation. */ -BleMedium::BleMedium(::nearby::api::BluetoothAdapter& adapter) - : adapter_(static_cast(&adapter)) {} - -bool BleMedium::StartAdvertising( - const BleAdvertisementData& advertising_data, - ::nearby::api::ble_v2::AdvertiseParameters advertise_set_parameters) { - if (advertising_data.service_data.empty()) { - return false; - } - const auto& service_uuid = advertising_data.service_data.begin()->first.Get16BitAsString(); - const ByteArray& service_data_bytes = advertising_data.service_data.begin()->second; - - if (!peripheral_) { - peripheral_ = [[GNCMBlePeripheral alloc] init]; - } - - auto& peripheral = adapter_->GetPeripheral(); - [peripheral_ - startAdvertisingWithServiceUUID:ObjCStringFromCppString(service_uuid) - advertisementData:NSDataFromByteArray(service_data_bytes) - endpointConnectedHandler:^GNCMConnectionHandlers*(id connection) { - // TODO(edwinwu): This server_socket is supposed to be gotten from the map by key of - // servcie_id. We now always get the first iteration since we don't know the key now. - // Try the way to move the Ble socket frame verification up to one layer. - std::string service_id; - BleServerSocket* server_socket; - if (!server_sockets_.empty()) { - service_id = server_sockets_.begin()->first; - server_socket = server_sockets_.begin()->second; - } else { - return nil; - } - auto socket = std::make_unique(connection, &peripheral); - GNCMConnectionHandlers* connectionHandlers = - static_cast(socket->GetInputStream()).GetConnectionHandlers(); - server_socket->Connect(std::move(socket)); - return connectionHandlers; - } - callbackQueue:callback_queue_]; - return true; -} - -bool BleMedium::StopAdvertising() { - peripheral_ = nil; - return true; -} -std::unique_ptr BleMedium::StartAdvertising( - const api::ble_v2::BleAdvertisementData& advertising_data, - api::ble_v2::AdvertiseParameters advertise_parameters, - BleMedium::AdvertisingCallback callback) { - // TODO(hais): add real impl for iOs StartAdvertising - return std::make_unique(AdvertisingSession{}); -} -bool BleMedium::StartScanning(const Uuid& service_uuid, TxPowerLevel tx_power_level, - ScanCallback scan_callback) { - if (!central_) { - central_ = [[GNCMBleCentral alloc] init]; - } - - __block ScanCallback callback = std::move(scan_callback); - [central_ startScanningWithServiceUUID:ObjCStringFromCppString(service_uuid.Get16BitAsString()) - scanResultHandler:^(NSString* peripheralID, NSData* serviceData) { - BleAdvertisementData advertisement_data; - advertisement_data.service_data = {{service_uuid, ByteArrayFromNSData(serviceData)}}; - BlePeripheral& peripheral = adapter_->GetPeripheral(); - peripheral.SetPeripheralId(CppStringFromObjCString(peripheralID)); - callback.advertisement_found_cb(peripheral, advertisement_data); - } - requestConnectionHandler:^(GNCMBleConnectionRequester connectionRequester) { - BlePeripheral& peripheral = adapter_->GetPeripheral(); - peripheral.SetConnectionRequester(connectionRequester); - } - callbackQueue:callback_queue_]; - - return true; -} - -bool BleMedium::StopScanning() { - central_ = nil; - return true; -} - -std::unique_ptr BleMedium::StartScanning( - const Uuid& service_uuid, TxPowerLevel tx_power_level, BleMedium::ScanningCallback callback) { - // TODO(hais): add real impl for windows StartScanning. - return std::make_unique(ScanningSession{}); -} - -std::unique_ptr BleMedium::StartGattServer( - api::ble_v2::ServerGattConnectionCallback callback) { - if (!peripheral_) { - peripheral_ = [[GNCMBlePeripheral alloc] init]; - } - return std::make_unique(adapter_, peripheral_); -} - -std::unique_ptr BleMedium::ConnectToGattServer( - api::ble_v2::BlePeripheral& peripheral, TxPowerLevel tx_power_level, - api::ble_v2::ClientGattConnectionCallback callback) { - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - __block NSError* connectedError; - BlePeripheral iosPeripheral = static_cast(peripheral); - std::string peripheral_id = iosPeripheral.GetPeripheralId(); - [central_ connectGattServerWithPeripheralID:ObjCStringFromCppString(peripheral_id) - gattConnectionResultHandler:^(NSError* _Nullable error) { - connectedError = error; - dispatch_semaphore_signal(semaphore); - }]; - dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC)); - - if (connectedError) { - return nullptr; - } - return std::make_unique(central_, peripheral_id); -} - -std::unique_ptr BleMedium::OpenServerSocket( - const std::string& service_id) { - auto server_socket = std::make_unique(); - server_socket->SetCloseNotifier([this, service_id]() { - absl::MutexLock lock(&mutex_); - server_sockets_.erase(service_id); - }); - absl::MutexLock lock(&mutex_); - server_sockets_.insert({service_id, server_socket.get()}); - return server_socket; -} - -std::unique_ptr BleMedium::Connect(const std::string& service_id, - TxPowerLevel tx_power_level, - api::ble_v2::BlePeripheral& peripheral, - CancellationFlag* cancellation_flag) { - NSString* serviceID = ObjCStringFromCppString(service_id); - __block std::unique_ptr socket; - __block BlePeripheral ios_peripheral = static_cast(peripheral); - GNCMBleConnectionRequester connection_requester = ios_peripheral.GetConnectionRequester(); - if (!connection_requester) return {}; - - dispatch_group_t group = dispatch_group_create(); - dispatch_group_enter(group); - if (cancellation_flag->Cancelled()) { - NSLog(@"[NEARBY] BLE Connect: Has been cancelled: service_id=%@", serviceID); - dispatch_group_leave(group); // unblock - return {}; - } - - connection_requester(serviceID, ^(id connection) { - // If the connection wasn't successfully established, return a NULL socket. - if (connection) { - socket = std::make_unique(connection, &ios_peripheral); - } - - dispatch_group_leave(group); // unblock - return socket != nullptr - ? static_cast(socket->GetInputStream()).GetConnectionHandlers() - : nullptr; - }); - dispatch_group_wait(group, DISPATCH_TIME_FOREVER); - - // Send the (empty) intro packet, which the BLE advertiser is expecting. - if (socket != nullptr) { - socket->GetOutputStream().Write(ByteArray()); - } - - return std::move(socket); -} - -bool BleMedium::IsExtendedAdvertisementsAvailable() { return false; } - -// NOLINTNEXTLINE -absl::optional BleMedium::GattServer::CreateCharacteristic( - const Uuid& service_uuid, const Uuid& characteristic_uuid, - api::ble_v2::GattCharacteristic::Permission permission, - api::ble_v2::GattCharacteristic::Property property) { - api::ble_v2::GattCharacteristic characteristic = {.uuid = characteristic_uuid, - .service_uuid = service_uuid, - .permission = permission, - .property = property}; - [peripheral_ - addCBServiceWithUUID:[CBUUID - UUIDWithString:ObjCStringFromCppString( - characteristic.service_uuid.Get16BitAsString())]]; - [peripheral_ - addCharacteristic:[[CBMutableCharacteristic alloc] - initWithType:[CBUUID UUIDWithString:ObjCStringFromCppString(std::string( - characteristic.uuid))] - properties:PropertiesToCBProperties(characteristic.property) - value:nil - permissions:PermissionToCBPermissions(characteristic.permission)]]; - return characteristic; -} - -bool BleMedium::GattServer::UpdateCharacteristic( - const api::ble_v2::GattCharacteristic& characteristic, const nearby::ByteArray& value) { - [peripheral_ updateValue:NSDataFromByteArray(value) - forCharacteristic:[CBUUID UUIDWithString:ObjCStringFromCppString( - std::string(characteristic.uuid))]]; - return true; -} - -absl::Status BleMedium::GattServer::NotifyCharacteristicChanged( - const api::ble_v2::GattCharacteristic& characteristic, bool confirm, - const ByteArray& new_value) { - // no-op because client cannot request notifications. - return absl::UnimplementedError("Unimplemented!"); -} - -void BleMedium::GattServer::Stop() { [peripheral_ stopGATTService]; } - -bool BleMedium::GattClient::DiscoverServiceAndCharacteristics( - const Uuid& service_uuid, const std::vector& characteristic_uuids) { - // Discover all characteristics that may contain the advertisement. - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - gatt_characteristic_values_.clear(); - CBUUID* serviceUUID = [CBUUID UUIDWithString:ObjCStringFromCppString(std::string(service_uuid))]; - - absl::flat_hash_map gatt_characteristics; - NSMutableArray* characteristicUUIDs = - [NSMutableArray arrayWithCapacity:characteristic_uuids.size()]; - for (const auto& characteristic_uuid : characteristic_uuids) { - [characteristicUUIDs addObject:[CBUUID UUIDWithString:ObjCStringFromCppString( - std::string(characteristic_uuid))]]; - gatt_characteristics.insert({std::string(characteristic_uuid), characteristic_uuid}); - } - - [central_ discoverGattService:serviceUUID - gattCharacteristics:characteristicUUIDs - peripheralID:ObjCStringFromCppString(peripheral_id_) - gattDiscoverResultHandler:^(NSOrderedSet* _Nullable cb_characteristics) { - if (cb_characteristics != nil) { - for (CBCharacteristic* cb_characteristic in cb_characteristics) { - auto const& it = gatt_characteristics.find( - CppStringFromObjCString(cb_characteristic.UUID.UUIDString)); - if (it == gatt_characteristics.end()) continue; - - api::ble_v2::GattCharacteristic characteristic = {.uuid = it->second, - .service_uuid = service_uuid}; - gatt_characteristic_values_.insert( - {characteristic, ByteArrayFromNSData(cb_characteristic.value).string_data()}); - } - } - - dispatch_semaphore_signal(semaphore); - }]; - - dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC)); - - if (gatt_characteristic_values_.empty()) { - return false; - } - return true; -} - -// NOLINTNEXTLINE -absl::optional BleMedium::GattClient::GetCharacteristic( - const Uuid& service_uuid, const Uuid& characteristic_uuid) { - api::ble_v2::GattCharacteristic characteristic = {.uuid = characteristic_uuid, - .service_uuid = service_uuid}; - auto const it = gatt_characteristic_values_.find(characteristic); - if (it == gatt_characteristic_values_.end()) { - return absl::nullopt; // NOLINT - } - return it->first; -} - -// NOLINTNEXTLINE -absl::optional BleMedium::GattClient::ReadCharacteristic( - const api::ble_v2::GattCharacteristic& characteristic) { - auto const it = gatt_characteristic_values_.find(characteristic); - if (it == gatt_characteristic_values_.end()) { - return absl::nullopt; // NOLINT - } - return it->second; -} - -bool BleMedium::GattClient::WriteCharacteristic( - const api::ble_v2::GattCharacteristic& characteristic, absl::string_view value, - api::ble_v2::GattClient::WriteType write_type) { - // No op. - return false; -} - -bool BleMedium::GattClient::SetCharacteristicSubscription( - const api::ble_v2::GattCharacteristic& characteristic, bool enable, - absl::AnyInvocable on_characteristic_changed_cb) { - // No op since we can't write characteristics. - return false; -} - -void BleMedium::GattClient::Disconnect() { - [central_ disconnectGattServiceWithPeripheralID:ObjCStringFromCppString(peripheral_id_)]; -} - -} // namespace apple -} // namespace nearby diff --git a/internal/platform/implementation/apple/ble_gatt_server.h b/internal/platform/implementation/apple/ble_gatt_server.h index e42d4998..abf5e8ea 100644 --- a/internal/platform/implementation/apple/ble_gatt_server.h +++ b/internal/platform/implementation/apple/ble_gatt_server.h @@ -71,7 +71,7 @@ class GattServer : public api::ble_v2::GattServer { private: GNCBLEGATTServer *gatt_server_; - BlePeripheral peripheral_; + EmptyBlePeripheral peripheral_; }; } // namespace apple diff --git a/internal/platform/implementation/apple/bluetooth_adapter.h b/internal/platform/implementation/apple/bluetooth_adapter.h deleted file mode 100644 index ce08753f..00000000 --- a/internal/platform/implementation/apple/bluetooth_adapter.h +++ /dev/null @@ -1,108 +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_PLATFORM_IMPLEMENTATION_APPLE_BLUETOOTH_ADAPTER_H_ -#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLUETOOTH_ADAPTER_H_ - -#include - -#import "internal/platform/implementation/apple/Mediums/Ble/GNCMBleCentral.h" -#include "internal/platform/implementation/ble_v2.h" -#include "internal/platform/implementation/bluetooth_adapter.h" -#include "internal/platform/prng.h" - -namespace nearby { -namespace apple { - -class BluetoothAdapter; - -// Concrete BlePeripheral implementation. -class BlePeripheral : public api::ble_v2::BlePeripheral { - public: - std::string GetAddress() const override; - - api::ble_v2::BlePeripheral::UniqueId GetUniqueId() const override { - return unique_id_; - } - - std::string GetPeripheralId() const { return peripheral_id_; } - - void SetPeripheralId(const std::string& peripheral_id) { - peripheral_id_ = peripheral_id; - } - - void SetConnectionRequester(GNCMBleConnectionRequester connection_requester) { - connection_requester_ = connection_requester; - } - - GNCMBleConnectionRequester GetConnectionRequester() { - return connection_requester_; - } - - private: - // Only BluetoothAdapter may instantiate BlePeripheral. - friend class BluetoothAdapter; - - explicit BlePeripheral(BluetoothAdapter* adapter) : adapter_(*adapter) { - unique_id_ = Prng().NextInt64(); - } - - BluetoothAdapter& adapter_; - std::string peripheral_id_; - GNCMBleConnectionRequester connection_requester_; - api::ble_v2::BlePeripheral::UniqueId unique_id_; -}; - -// Concrete BluetoothAdapter implementation. -class BluetoothAdapter : public api::BluetoothAdapter { - public: - using Status = api::BluetoothAdapter::Status; - using ScanMode = api::BluetoothAdapter::ScanMode; - - ~BluetoothAdapter() override { SetStatus(Status::kDisabled); } - - bool SetStatus(Status status) override { - enabled_ = status == Status::kEnabled; - return true; - } - bool IsEnabled() const override { return enabled_; } - ScanMode GetScanMode() const override { return mode_; } - bool SetScanMode(ScanMode mode) override { return false; } - std::string GetName() const override { return name_; } - bool SetName(absl::string_view name) { - return SetName(name, /* persist= */ true); - } - bool SetName(absl::string_view name, bool persist) override { - name_ = std::string(name); - return true; - } - std::string GetMacAddress() const override { return mac_address_; } - void SetMacAddress(absl::string_view mac_address) { - mac_address_ = std::string(mac_address); - } - - BlePeripheral& GetPeripheral() { return peripheral_; } - - private: - BlePeripheral peripheral_{this}; - ScanMode mode_ = ScanMode::kNone; - std::string name_; - std::string mac_address_; - bool enabled_ = true; -}; - -} // namespace apple -} // namespace nearby - -#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_BLUETOOTH_ADAPTER_H_ diff --git a/internal/platform/implementation/apple/bluetooth_adapter.mm b/internal/platform/implementation/apple/bluetooth_adapter.mm deleted file mode 100644 index 7369e63c..00000000 --- a/internal/platform/implementation/apple/bluetooth_adapter.mm +++ /dev/null @@ -1,28 +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/platform/implementation/apple/bluetooth_adapter.h" - -#include - -#include "absl/strings/string_view.h" -#include "internal/platform/implementation/bluetooth_adapter.h" - -namespace nearby { -namespace apple { - -std::string BlePeripheral::GetAddress() const { return adapter_.GetMacAddress(); } - -} // namespace apple -} // namespace nearby diff --git a/internal/platform/implementation/apple/platform.mm b/internal/platform/implementation/apple/platform.mm index b70fc53a..781a50ea 100644 --- a/internal/platform/implementation/apple/platform.mm +++ b/internal/platform/implementation/apple/platform.mm @@ -170,7 +170,7 @@ std::unique_ptr ImplementationPlatform::CreateBleMedium(api::Bluetoot std::unique_ptr ImplementationPlatform::CreateBleV2Medium( api::BluetoothAdapter& adapter) { - return std::make_unique(adapter); + return std::make_unique(); } std::unique_ptr ImplementationPlatform::CreateServerSyncMedium() { From 91fb4a9a3ef02bf9663e2970f9da85bb89b565a4 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Thu, 24 Aug 2023 12:43:17 -0700 Subject: [PATCH 125/128] Add stop advertising and stop scanning BLE implementation for Apple platforms PiperOrigin-RevId: 559838443 --- .../apple/Mediums/BLEv2/GNCBLEGATTServer.h | 37 +++++++++++++++ .../apple/Mediums/BLEv2/GNCBLEGATTServer.m | 11 +++++ .../apple/Mediums/BLEv2/GNCBLEMedium.h | 32 +++++++++++++ .../apple/Mediums/BLEv2/GNCBLEMedium.m | 25 ++++++++++ .../apple/Tests/GNCBLEGATTServerTest.m | 40 ++++++++++++++++ .../apple/Tests/GNCBLEMediumTest.m | 46 +++++++++++++++++++ .../apple/Tests/GNCFakePeripheralManager.m | 1 + .../implementation/apple/ble_medium.h | 4 -- .../implementation/apple/ble_medium.mm | 30 ++++++++++-- 9 files changed, 218 insertions(+), 8 deletions(-) diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h index 2461d5f6..1b94731a 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.h @@ -19,11 +19,39 @@ NS_ASSUME_NONNULL_BEGIN +/** + * A block to be invoked when a call to + * @c createCharacteristicWithServiceID:characteristicUUID:permissions:properties:completionHandler: + * has completed. + * + * @param characteristic The created characteristic, or @c nil if an error occurred. + * @param error The cause of the failure, or @c nil if no error occurred. + */ typedef void (^GNCCreateCharacteristicCompletionHandler)( GNCBLEGATTCharacteristic *_Nullable characteristic, NSError *_Nullable error); + +/** + * A block to be invoked when a call to @c updateCharacteristic:value:completionHandler: has + * completed. + * + * @param error The cause of the failure, or @c nil if no error occurred. + */ typedef void (^GNCUpdateCharacteristicCompletionHandler)(NSError *_Nullable error); + +/** + * A block to be invoked when a call to @c startAdvertisingData:completionHandler: has completed. + * + * @param error The cause of the failure, or @c nil if no error occurred. + */ typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error); +/** + * A block to be invoked when a call to @c stopAdvertisingWithcompletionHandler: has completed. + * + * @param error The cause of the failure, or @c nil if no error occurred. + */ +typedef void (^GNCStopAdvertisingCompletionHandler)(NSError *_Nullable error); + /** * An object that manages and advertises GATT characteritics. * @@ -82,6 +110,15 @@ typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error); - (void)startAdvertisingData:(NSDictionary *)serviceData completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler; +/** + * Stops advertising all service data. + * + * @param completionHandler Called on a private queue with @c nil if successfully stopped + * advertising or an error if one has occured. + */ +- (void)stopAdvertisingWithCompletionHandler: + (nullable GNCStopAdvertisingCompletionHandler)completionHandler; + @end NS_ASSUME_NONNULL_END diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m index 1f996d69..703f6edd 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTServer.m @@ -223,6 +223,17 @@ static char *const kGNCBLEGATTServerQueueLabel = "com.nearby.GNCBLEGATTServer"; }); } +- (void)stopAdvertisingWithCompletionHandler: + (nullable GNCStopAdvertisingCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + _advertisementData = nil; + [_peripheralManager stopAdvertising]; + if (completionHandler) { + completionHandler(nil); + } + }); +} + #pragma mark - Internal - (void)internalAddPendingServicesIfPoweredOn { diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h index d81b4975..51574091 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.h @@ -30,6 +30,13 @@ NS_ASSUME_NONNULL_BEGIN */ typedef void (^GNCStartAdvertisingCompletionHandler)(NSError *_Nullable error); +/** + * A block to be invoked when a call to @c stopAdvertisingWithcompletionHandler: has completed. + * + * @param error The cause of the failure, or @c nil if no error occurred. + */ +typedef void (^GNCStopAdvertisingCompletionHandler)(NSError *_Nullable error); + /** * A block to be invoked when a peripheral’s advertisement has been discovered. * @@ -50,6 +57,13 @@ typedef void (^GNCAdvertisementFoundHandler)(id peripheral, */ typedef void (^GNCStartScanningCompletionHandler)(NSError *_Nullable error); +/** + * A block to be invoked when a call to @c stopScanningWithCompletionHandler: has completed. + * + * @param error The cause of the failure, or @c nil if no error occurred. + */ +typedef void (^GNCStopScanningCompletionHandler)(NSError *_Nullable error); + /** * A block to be invoked when a call to @c startGATTServerWithCompletionHandler: has completed. * @@ -100,6 +114,15 @@ typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable c - (void)startAdvertisingData:(NSDictionary *)serviceData completionHandler:(nullable GNCStartAdvertisingCompletionHandler)completionHandler; +/** + * Stops advertising all service data. + * + * @param completionHandler Called on a private queue with @c nil if successfully stopped + * advertising or an error if one has occured. + */ +- (void)stopAdvertisingWithCompletionHandler: + (nullable GNCStopAdvertisingCompletionHandler)completionHandler; + /** * Scans for peripherals that are advertising the specified service. * @@ -112,6 +135,15 @@ typedef void (^GNCGATTConnectionCompletionHandler)(GNCBLEGATTClient *_Nullable c advertisementFoundHandler:(GNCAdvertisementFoundHandler)advertisementFoundHandler completionHandler:(nullable GNCStartScanningCompletionHandler)completionHandler; +/** + * Stops scanning for peripherals. + * + * @param completionHandler Called on a private queue with @c nil if successfully stopped + * scanning or an error if one has occured. + */ +- (void)stopScanningWithCompletionHandler: + (nullable GNCStopScanningCompletionHandler)completionHandler; + /** * Starts a GATT server. * diff --git a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m index 7f1c0735..139b5169 100644 --- a/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m +++ b/internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEMedium.m @@ -101,6 +101,19 @@ static NSError *AlreadyScanningError() { }); } +- (void)stopAdvertisingWithCompletionHandler: + (nullable GNCStopAdvertisingCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + if (!_server) { + if (completionHandler) { + completionHandler(nil); + } + return; + } + [_server stopAdvertisingWithCompletionHandler:completionHandler]; + }); +} + - (void)startScanningForService:(CBUUID *)serviceUUID advertisementFoundHandler:(GNCAdvertisementFoundHandler)advertisementFoundHandler completionHandler:(nullable GNCStartScanningCompletionHandler)completionHandler { @@ -122,6 +135,18 @@ static NSError *AlreadyScanningError() { }); } +- (void)stopScanningWithCompletionHandler: + (nullable GNCStopScanningCompletionHandler)completionHandler { + dispatch_async(_queue, ^{ + _serviceUUID = nil; + _advertisementFoundHandler = nil; + [_centralManager stopScan]; + if (completionHandler) { + completionHandler(nil); + } + }); +} + - (void)startGATTServerWithCompletionHandler: (nullable GNCGATTServerCompletionHandler)completionHandler { dispatch_async(_queue, ^{ diff --git a/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m b/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m index a016a47f..ada28625 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m +++ b/internal/platform/implementation/apple/Tests/GNCBLEGATTServerTest.m @@ -614,4 +614,44 @@ static NSString *const kCharacteristicUUID2 = @"00000000-0000-3000-8000-00000000 [self waitForExpectations:@[ expectation ] timeout:3]; } +- (void)testStartStopStartAdvertising { + GNCFakePeripheralManager *fakePeripheralManager = [[GNCFakePeripheralManager alloc] init]; + + GNCBLEGATTServer *gattServer = + [[GNCBLEGATTServer alloc] initWithPeripheralManager:fakePeripheralManager]; + + [fakePeripheralManager simulatePeripheralManagerDidUpdateState:CBManagerStatePoweredOn]; + + XCTestExpectation *expectation = + [[XCTestExpectation alloc] initWithDescription:@"Start advertising."]; + + [gattServer startAdvertisingData:@{[CBUUID UUIDWithString:@"FEF3"] : [NSData data]} + completionHandler:^(NSError *error) { + XCTAssertNil(error); + XCTAssertTrue(fakePeripheralManager.isAdvertising); + NSDictionary *data = fakePeripheralManager.advertisementData; + XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @""); + XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], + [CBUUID UUIDWithString:@"FEF3"]); + [gattServer stopAdvertisingWithCompletionHandler:^(NSError *error) { + XCTAssertNil(error); + XCTAssertFalse(fakePeripheralManager.isAdvertising); + [gattServer + startAdvertisingData:@{[CBUUID UUIDWithString:@"FEF4"] : [NSData data]} + completionHandler:^(NSError *error) { + XCTAssertNil(error); + XCTAssertTrue(fakePeripheralManager.isAdvertising); + NSDictionary *data = + fakePeripheralManager.advertisementData; + XCTAssertEqualObjects(data[CBAdvertisementDataLocalNameKey], @""); + XCTAssertEqualObjects(data[CBAdvertisementDataServiceUUIDsKey][0], + [CBUUID UUIDWithString:@"FEF4"]); + [expectation fulfill]; + }]; + }]; + }]; + + [self waitForExpectations:@[ expectation ] timeout:3]; +} + @end diff --git a/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m b/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m index e71f5fc5..47121133 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m +++ b/internal/platform/implementation/apple/Tests/GNCBLEMediumTest.m @@ -106,6 +106,36 @@ static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB"; [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 { @@ -226,6 +256,22 @@ static NSString *const kServiceUUID = @"0000FEF3-0000-1000-8000-00805F9B34FB"; [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 - Connect - (void)testSuccessfulConnect { diff --git a/internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.m b/internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.m index 184e5688..297730fa 100644 --- a/internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.m +++ b/internal/platform/implementation/apple/Tests/GNCFakePeripheralManager.m @@ -119,6 +119,7 @@ } - (void)stopAdvertising { + _isAdvertising = false; } #pragma mark - Testing Helpers diff --git a/internal/platform/implementation/apple/ble_medium.h b/internal/platform/implementation/apple/ble_medium.h index 2bfc5f9c..049e0a59 100644 --- a/internal/platform/implementation/apple/ble_medium.h +++ b/internal/platform/implementation/apple/ble_medium.h @@ -67,8 +67,6 @@ class BleMedium : public api::ble_v2::BleMedium { bool StartAdvertising(const api::ble_v2::BleAdvertisementData &advertising_data, api::ble_v2::AdvertiseParameters advertise_set_parameters) override; - // TODO(b/290385712): Not yet implemented. - // // Stops advertising. // // Returns whether or not advertising was successfully stopped. @@ -92,8 +90,6 @@ class BleMedium : public api::ble_v2::BleMedium { bool StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, api::ble_v2::BleMedium::ScanCallback callback) override; - // TODO(b/290385712): Not yet implemented. - // // Stops scanning. // // Returns whether or not scanning was successfully stopped. diff --git a/internal/platform/implementation/apple/ble_medium.mm b/internal/platform/implementation/apple/ble_medium.mm index aee27ff9..35d555aa 100644 --- a/internal/platform/implementation/apple/ble_medium.mm +++ b/internal/platform/implementation/apple/ble_medium.mm @@ -87,8 +87,19 @@ bool BleMedium::StartAdvertising(const api::ble_v2::BleAdvertisementData &advert return blockError == nil; } -// TODO(b/290385712): Implement. -bool BleMedium::StopAdvertising() { return false; } +bool BleMedium::StopAdvertising() { + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block NSError *blockError = nil; + [medium_ stopAdvertisingWithCompletionHandler:^(NSError *error) { + if (error != nil) { + GTMLoggerError(@"Failed to stop advertising: %@", error); + } + blockError = error; + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + return blockError == nil; +} // TODO(b/290385712): Implement. std::unique_ptr BleMedium::StartScanning( @@ -139,8 +150,19 @@ bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLeve return blockError == nil; } -// TODO(b/290385712): Implement. -bool BleMedium::StopScanning() { return false; } +bool BleMedium::StopScanning() { + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block NSError *blockError = nil; + [medium_ stopScanningWithCompletionHandler:^(NSError *error) { + if (error != nil) { + GTMLoggerError(@"Failed to stop scanning: %@", error); + } + blockError = error; + dispatch_semaphore_signal(semaphore); + }]; + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + return blockError == nil; +} // TODO(b/290385712): Add implementation that calls ServerGattConnectionCallback methods. std::unique_ptr BleMedium::StartGattServer( From 965cc0ecbcdbc0a2109dd5fc12d62310f481e402 Mon Sep 17 00:00:00 2001 From: Nick Bourdakos Date: Thu, 24 Aug 2023 14:06:38 -0700 Subject: [PATCH 126/128] Add implementation for async BLE start scan/advertise PiperOrigin-RevId: 559863656 --- .../apple/Tests/GNCBLEUtilsTest.mm | 18 ++++ .../implementation/apple/ble_medium.h | 10 +- .../implementation/apple/ble_medium.mm | 91 +++++++++++++------ .../platform/implementation/apple/ble_utils.h | 3 + .../implementation/apple/ble_utils.mm | 12 +++ 5 files changed, 103 insertions(+), 31 deletions(-) diff --git a/internal/platform/implementation/apple/Tests/GNCBLEUtilsTest.mm b/internal/platform/implementation/apple/Tests/GNCBLEUtilsTest.mm index 4434a2b2..1cc73ecb 100644 --- a/internal/platform/implementation/apple/Tests/GNCBLEUtilsTest.mm +++ b/internal/platform/implementation/apple/Tests/GNCBLEUtilsTest.mm @@ -13,6 +13,7 @@ // limitations under the License. #import +#import #import #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" @@ -25,6 +26,7 @@ using Property = ::nearby::api::ble_v2::GattCharacteristic::Property; using Permission = ::nearby::api::ble_v2::GattCharacteristic::Permission; using WriteType = ::nearby::api::ble_v2::GattClient::WriteType; using Uuid = ::nearby::Uuid; +using ByteArray = ::nearby::ByteArray; @interface GNCBLEUtilsTest : XCTestCase @end @@ -120,6 +122,22 @@ using Uuid = ::nearby::Uuid; XCTAssertEqual(actual.properties, properties); } +- (void)testObjCServiceDataFromCpp { + CBUUID *serviceUUID1 = [CBUUID UUIDWithString:@"0000FEF3-0000-1000-8000-00805F9B34FB"]; + CBUUID *serviceUUID2 = [CBUUID UUIDWithString:@"0000FEF4-0000-1000-8000-00805F9B34FB"]; + NSData *data1 = [@"one" dataUsingEncoding:NSUTF8StringEncoding]; + NSData *data2 = [@"two" dataUsingEncoding:NSUTF8StringEncoding]; + + absl::flat_hash_map service_data; + service_data[Uuid(0x0000FEF300001000, 0x800000805F9B34FB)] = ByteArray("one"); + service_data[Uuid(0x0000FEF400001000, 0x800000805F9B34FB)] = ByteArray("two"); + + NSMutableDictionary *actual = + ::nearby::apple::ObjCServiceDataFromCPP(service_data); + XCTAssertEqualObjects(actual[serviceUUID1], data1); + XCTAssertEqualObjects(actual[serviceUUID2], data2); +} + #pragma mark - Objective-C to C++ - (void)testCPPUUIDFromObjC16Bit { diff --git a/internal/platform/implementation/apple/ble_medium.h b/internal/platform/implementation/apple/ble_medium.h index 049e0a59..7b00fed3 100644 --- a/internal/platform/implementation/apple/ble_medium.h +++ b/internal/platform/implementation/apple/ble_medium.h @@ -48,8 +48,6 @@ class BleMedium : public api::ble_v2::BleMedium { BleMedium(); ~BleMedium() override = default; - // TODO(b/290385712): Not yet implemented. - // // Async interface for StartAdvertising. // // Result status will be passed to start_advertising_result callback. To stop advertising, invoke @@ -72,8 +70,6 @@ class BleMedium : public api::ble_v2::BleMedium { // Returns whether or not advertising was successfully stopped. bool StopAdvertising() override; - // TODO(b/290385712): Not yet implemented. - // // Async interface for StartScanning. // // Result status will be passed to start_scanning_result callback on a private queue. To stop @@ -147,6 +143,12 @@ class BleMedium : public api::ble_v2::BleMedium { api::ble_v2::BleMedium::GetRemotePeripheralCallback callback) override; private: + void HandleAdvertisementFound( + id peripheral, NSDictionary *serviceData, + absl::AnyInvocable + callback); + GNCBLEMedium *medium_; absl::Mutex peripherals_mutex_; diff --git a/internal/platform/implementation/apple/ble_medium.mm b/internal/platform/implementation/apple/ble_medium.mm index 35d555aa..ae64d334 100644 --- a/internal/platform/implementation/apple/ble_medium.mm +++ b/internal/platform/implementation/apple/ble_medium.mm @@ -56,22 +56,31 @@ namespace apple { BleMedium::BleMedium() : medium_([[GNCBLEMedium alloc] init]) {} -// TODO(b/290385712): Implement. std::unique_ptr BleMedium::StartAdvertising( const api::ble_v2::BleAdvertisementData &advertising_data, api::ble_v2::AdvertiseParameters advertise_set_parameters, api::ble_v2::BleMedium::AdvertisingCallback callback) { - return nullptr; + NSMutableDictionary *serviceData = + ObjCServiceDataFromCPP(advertising_data.service_data); + + __block api::ble_v2::BleMedium::AdvertisingCallback blockCallback = std::move(callback); + + [medium_ startAdvertisingData:serviceData + completionHandler:^(NSError *error) { + blockCallback.start_advertising_result( + error == nil ? absl::OkStatus() + : absl::InternalError(error.localizedDescription.UTF8String)); + }]; + + return std::make_unique(AdvertisingSession{.stop_advertising = [this] { + return StopAdvertising() ? absl::OkStatus() : absl::InternalError("Failed to stop advertising"); + }}); } bool BleMedium::StartAdvertising(const api::ble_v2::BleAdvertisementData &advertising_data, api::ble_v2::AdvertiseParameters advertise_set_parameters) { - NSMutableDictionary *serviceData = [[NSMutableDictionary alloc] init]; - for (const auto &pair : advertising_data.service_data) { - CBUUID *key = CBUUID16FromCPP(pair.first); - NSData *data = NSDataFromByteArray(pair.second); - [serviceData setObject:data forKey:key]; - } + NSMutableDictionary *serviceData = + ObjCServiceDataFromCPP(advertising_data.service_data); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); __block NSError *blockError = nil; @@ -101,11 +110,54 @@ bool BleMedium::StopAdvertising() { return blockError == nil; } -// TODO(b/290385712): Implement. +void BleMedium::HandleAdvertisementFound( + id peripheral, NSDictionary *serviceData, + absl::AnyInvocable + callback) { + absl::MutexLock lock(&peripherals_mutex_); + [socketCentralManager_ retrievePeripheralWithIdentifier:peripheral.identifier + advertisementData:@{}]; + + api::ble_v2::BleAdvertisementData data; + for (CBUUID *key in serviceData.allKeys) { + data.service_data[CPPUUIDFromObjC(key)] = ByteArrayFromNSData(serviceData[key]); + } + + // Add the peripheral to the map if we haven't discovered it yet. + auto ble_peripheral = std::make_unique(peripheral); + auto unique_id = ble_peripheral->GetUniqueId(); + auto it = peripherals_.find(unique_id); + if (it == peripherals_.end()) { + peripherals_[unique_id] = std::move(ble_peripheral); + } + callback(*peripherals_[unique_id], data); +} + std::unique_ptr BleMedium::StartScanning( const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, api::ble_v2::BleMedium::ScanningCallback callback) { - return nullptr; + CBUUID *serviceUUID = CBUUID128FromCPP(service_uuid); + __block api::ble_v2::BleMedium::ScanningCallback blockCallback = std::move(callback); + + socketCentralManager_ = [[GNSCentralManager alloc] initWithSocketServiceUUID:serviceUUID]; + [socketCentralManager_ startNoScanModeWithAdvertisedServiceUUIDs:@[ serviceUUID ]]; + + [medium_ startScanningForService:serviceUUID + advertisementFoundHandler:^(id peripheral, + NSDictionary *serviceData) { + HandleAdvertisementFound(peripheral, serviceData, + std::move(blockCallback.advertisement_found_cb)); + } + completionHandler:^(NSError *error) { + blockCallback.start_scanning_result( + error == nil ? absl::OkStatus() + : absl::InternalError(error.localizedDescription.UTF8String)); + }]; + + return std::make_unique(ScanningSession{.stop_scanning = [this] { + return StopScanning() ? absl::OkStatus() : absl::InternalError("Failed to stop scanning"); + }}); } bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLevel tx_power_level, @@ -121,23 +173,8 @@ bool BleMedium::StartScanning(const Uuid &service_uuid, api::ble_v2::TxPowerLeve [medium_ startScanningForService:serviceUUID advertisementFoundHandler:^(id peripheral, NSDictionary *serviceData) { - absl::MutexLock lock(&peripherals_mutex_); - [socketCentralManager_ retrievePeripheralWithIdentifier:peripheral.identifier - advertisementData:@{}]; - - api::ble_v2::BleAdvertisementData data; - for (CBUUID *key in serviceData.allKeys) { - data.service_data[CPPUUIDFromObjC(key)] = ByteArrayFromNSData(serviceData[key]); - } - - // Add the peripheral to the map if we haven't discovered it yet. - auto ble_peripheral = std::make_unique(peripheral); - auto unique_id = ble_peripheral->GetUniqueId(); - auto it = peripherals_.find(unique_id); - if (it == peripherals_.end()) { - peripherals_[unique_id] = std::move(ble_peripheral); - } - blockCallback.advertisement_found_cb(*peripherals_[unique_id], data); + HandleAdvertisementFound(peripheral, serviceData, + std::move(blockCallback.advertisement_found_cb)); } completionHandler:^(NSError *error) { if (error != nil) { diff --git a/internal/platform/implementation/apple/ble_utils.h b/internal/platform/implementation/apple/ble_utils.h index 209298fb..4477c403 100644 --- a/internal/platform/implementation/apple/ble_utils.h +++ b/internal/platform/implementation/apple/ble_utils.h @@ -56,6 +56,9 @@ CBCharacteristicProperties CBCharacteristicPropertiesFromCPP( /** Converts a C++ characteristic to an Objective-C characteristic. */ GNCBLEGATTCharacteristic *ObjCGATTCharacteristicFromCPP(const api::ble_v2::GattCharacteristic &c); +NSMutableDictionary *ObjCServiceDataFromCPP( + const absl::flat_hash_map &sd); + /** Converts a 16 or 128 bit CoreBluetooth UUID to a C++ UUID. */ Uuid CPPUUIDFromObjC(CBUUID *uuid); diff --git a/internal/platform/implementation/apple/ble_utils.mm b/internal/platform/implementation/apple/ble_utils.mm index cf08649e..cd9f3cdb 100644 --- a/internal/platform/implementation/apple/ble_utils.mm +++ b/internal/platform/implementation/apple/ble_utils.mm @@ -20,6 +20,7 @@ #include #import "internal/platform/implementation/apple/Mediums/BLEv2/GNCBLEGATTCharacteristic.h" +#import "internal/platform/implementation/apple/utils.h" namespace nearby { namespace apple { @@ -98,6 +99,17 @@ GNCBLEGATTCharacteristic *ObjCGATTCharacteristicFromCPP(const GattCharacteristic properties:properties]; } +NSMutableDictionary *ObjCServiceDataFromCPP( + const absl::flat_hash_map &sd) { + NSMutableDictionary *serviceData = [NSMutableDictionary dictionary]; + for (const auto &pair : sd) { + CBUUID *key = CBUUID16FromCPP(pair.first); + NSData *data = NSDataFromByteArray(pair.second); + [serviceData setObject:data forKey:key]; + } + return serviceData; +} + Uuid CPPUUIDFromObjC(CBUUID *uuid) { NSString *uuidString = uuid.UUIDString; if (uuidString.length == 4) { From 55aedfb235424b68407b7ec18cd935f0e81f66ec Mon Sep 17 00:00:00 2001 From: hai007 Date: Fri, 25 Aug 2023 09:50:16 -0700 Subject: [PATCH 127/128] Implement Safe to Disconnect feature PiperOrigin-RevId: 560119556 --- connections/implementation/BUILD | 4 + .../implementation/base_endpoint_channel.h | 3 +- .../implementation/base_pcp_handler.cc | 56 +++- connections/implementation/base_pcp_handler.h | 7 +- .../implementation/base_pcp_handler_test.cc | 16 +- connections/implementation/bwu_manager.cc | 11 +- connections/implementation/bwu_manager.h | 3 +- .../implementation/bwu_manager_test.cc | 161 ++++++++--- connections/implementation/client_proxy.cc | 49 +++- connections/implementation/client_proxy.h | 17 ++ .../implementation/client_proxy_test.cc | 11 +- .../endpoint_channel_manager.cc | 170 +++++++++++- .../implementation/endpoint_channel_manager.h | 60 +++- .../endpoint_channel_manager_test.cc | 15 +- .../implementation/endpoint_manager.cc | 260 ++++++++++++++---- connections/implementation/endpoint_manager.h | 22 +- .../implementation/endpoint_manager_test.cc | 40 ++- .../flags/nearby_connections_feature_flags.h | 19 +- connections/implementation/fuzzers/BUILD | 1 + connections/implementation/offline_frames.cc | 17 +- connections/implementation/offline_frames.h | 4 +- .../implementation/offline_frames_test.cc | 20 ++ .../offline_service_controller_test.cc | 3 + connections/implementation/payload_manager.cc | 190 ++++++++++++- connections/implementation/payload_manager.h | 31 ++- connections/implementation/simulation_user.h | 26 +- internal/platform/feature_flags.h | 14 + 27 files changed, 1052 insertions(+), 178 deletions(-) diff --git a/connections/implementation/BUILD b/connections/implementation/BUILD index 6c06e435..649b57f2 100644 --- a/connections/implementation/BUILD +++ b/connections/implementation/BUILD @@ -131,6 +131,7 @@ cc_library( "//internal/platform/implementation:comm", "//internal/platform/implementation:platform", "//internal/platform/implementation/shared:file", + "//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", @@ -182,7 +183,9 @@ cc_library( deps = [ ":internal", "//connections:core_types", + "//connections/implementation/flags:connections_flags", "//connections/v3:v3_types", + "//internal/flags:nearby_flags", "//internal/platform:base", "//internal/platform:test_util", "//internal/platform:types", @@ -240,6 +243,7 @@ cc_test( "//internal/platform:test_util", "//internal/platform:types", "//internal/platform/implementation/g3", # build_cleaner: keep + "//internal/proto/analytics:connections_log_cc_proto", "//internal/test", "//proto:connections_enums_cc_proto", "@com_github_protobuf_matchers//protobuf-matchers", diff --git a/connections/implementation/base_endpoint_channel.h b/connections/implementation/base_endpoint_channel.h index 5259748c..889cf4fa 100644 --- a/connections/implementation/base_endpoint_channel.h +++ b/connections/implementation/base_endpoint_channel.h @@ -21,9 +21,10 @@ #include "absl/base/thread_annotations.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" +#include "internal/platform/exception.h" #include "internal/platform/input_stream.h" #include "internal/platform/mutex.h" #include "internal/platform/output_stream.h" diff --git a/connections/implementation/base_pcp_handler.cc b/connections/implementation/base_pcp_handler.cc index 0e8af5be..085121d8 100644 --- a/connections/implementation/base_pcp_handler.cc +++ b/connections/implementation/base_pcp_handler.cc @@ -28,6 +28,8 @@ #include "absl/types/span.h" #include "connections/advertising_options.h" #include "connections/connection_options.h" +#include "connections/implementation/endpoint_channel_manager.h" +#include "connections/implementation/client_proxy.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/mediums/utils.h" #include "connections/implementation/offline_frames.h" @@ -1012,9 +1014,12 @@ void BasePcpHandler::ProcessPreConnectionInitiationFailure( } void BasePcpHandler::ProcessPreConnectionResultFailure( - ClientProxy* client, const std::string& endpoint_id) { + ClientProxy* client, const std::string& endpoint_id, + bool should_call_disconnect_endpoint, const DisconnectionReason& reason) { auto item = pending_connections_.extract(endpoint_id); - endpoint_manager_->DiscardEndpoint(client, endpoint_id); + if (should_call_disconnect_endpoint) { + endpoint_manager_->DiscardEndpoint(client, endpoint_id, reason); + } client->OnConnectionRejected(endpoint_id, {Status::kError}); } @@ -1048,7 +1053,9 @@ Status BasePcpHandler::AcceptConnection(ClientProxy* client, NEARBY_LOGS(ERROR) << "Channel destroyed before Accept; bring down " "connection: endpoint_id=" << endpoint_id; - ProcessPreConnectionResultFailure(client, endpoint_id); + ProcessPreConnectionResultFailure( + client, endpoint_id, /* should_call_disconnect_endpoint= */ true, + DisconnectionReason::IO_ERROR); response.Set({Status::kEndpointUnknown}); return; } @@ -1060,7 +1067,9 @@ Status BasePcpHandler::AcceptConnection(ClientProxy* client, NEARBY_LOGS(INFO) << "AcceptConnection: failed to send response: endpoint_id=" << endpoint_id; - ProcessPreConnectionResultFailure(client, endpoint_id); + ProcessPreConnectionResultFailure( + client, endpoint_id, /* should_call_disconnect_endpoint= */ true, + DisconnectionReason::IO_ERROR); response.Set({Status::kEndpointIoError}); return; } @@ -1106,7 +1115,9 @@ Status BasePcpHandler::RejectConnection(ClientProxy* client, << "Channel destroyed before Reject; bring down connection: " "endpoint_id=" << endpoint_id; - ProcessPreConnectionResultFailure(client, endpoint_id); + ProcessPreConnectionResultFailure( + client, endpoint_id, /* should_call_disconnect_endpoint= */ true, + DisconnectionReason::IO_ERROR); response.Set({Status::kEndpointUnknown}); return; } @@ -1118,7 +1129,9 @@ Status BasePcpHandler::RejectConnection(ClientProxy* client, NEARBY_LOGS(INFO) << "RejectConnection: failed to send response: endpoint_id=" << endpoint_id; - ProcessPreConnectionResultFailure(client, endpoint_id); + ProcessPreConnectionResultFailure( + client, endpoint_id, /* should_call_disconnect_endpoint= */ true, + DisconnectionReason::IO_ERROR); response.Set({Status::kEndpointIoError}); return; } @@ -1182,6 +1195,16 @@ void BasePcpHandler::OnIncomingFrame( client->SetRemoteOsInfo(endpoint_id, connection_response.os_info()); } + if (connection_response.has_safe_to_disconnect_version()) { + NEARBY_LOGS(INFO) + << "[safe-to-disconnect]: endpoint_id=" << endpoint_id + << "; Version = " + << connection_response.safe_to_disconnect_version(); + client->SetRemoteSafeToDisconnectVersion( + endpoint_id, connection_response.safe_to_disconnect_version()); + } + channel_manager_->UpdateSafeToDisconnectForEndpoint(endpoint_id, + client->IsSafeToDisconnectEnabled(endpoint_id)); EvaluateConnectionResult(client, endpoint_id, /* can_close_immediately= */ true); @@ -1193,13 +1216,14 @@ void BasePcpHandler::OnIncomingFrame( void BasePcpHandler::OnEndpointDisconnect(ClientProxy* client, const std::string& service_id, const std::string& endpoint_id, - CountDownLatch barrier) { + CountDownLatch barrier, + DisconnectionReason reason) { if (stop_.Get()) { barrier.CountDown(); return; } RunOnPcpHandlerThread("on-endpoint-disconnect", - [this, client, endpoint_id, barrier]() + [this, client, endpoint_id, barrier, reason]() RUN_ON_PCP_HANDLER_THREAD() mutable { auto item = pending_alarms_.find(endpoint_id); if (item != pending_alarms_.end()) { @@ -1207,8 +1231,10 @@ void BasePcpHandler::OnEndpointDisconnect(ClientProxy* client, alarm->Cancel(); pending_alarms_.erase(item); } - ProcessPreConnectionResultFailure(client, - endpoint_id); + ProcessPreConnectionResultFailure( + client, endpoint_id, + /* should_call_disconnect_endpoint= */ false, + reason); barrier.CountDown(); }); } @@ -1651,7 +1677,9 @@ void BasePcpHandler::ProcessTieBreakLoss( client, info->channel->GetMedium(), endpoint_id, info->channel.get(), info->is_incoming, info->start_time, {Status::kEndpointIoError}, info->result.lock().get()); - ProcessPreConnectionResultFailure(client, endpoint_id); + ProcessPreConnectionResultFailure(client, endpoint_id, + /* should_call_disconnect_endpoint= */ true, + DisconnectionReason::IO_ERROR); } bool BasePcpHandler::AppendRemoteBluetoothMacAddressEndpoint( @@ -1798,14 +1826,16 @@ void BasePcpHandler::EvaluateConnectionResult(ClientProxy* client, // Clean up the channel in EndpointManager if it's no longer required. if (can_close_immediately) { - endpoint_manager_->DiscardEndpoint(client, endpoint_id); + endpoint_manager_->DiscardEndpoint(client, endpoint_id, + DisconnectionReason::UNFINISHED); } else { pending_alarms_.emplace( endpoint_id, std::make_unique( "BasePcpHandler.evaluateConnectionResult() delayed close", [this, client, endpoint_id]() { - endpoint_manager_->DiscardEndpoint(client, endpoint_id); + endpoint_manager_->DiscardEndpoint( + client, endpoint_id, DisconnectionReason::UNFINISHED); }, kRejectedConnectionCloseDelay, &alarm_executor_)); } diff --git a/connections/implementation/base_pcp_handler.h b/connections/implementation/base_pcp_handler.h index 38edf662..07496504 100644 --- a/connections/implementation/base_pcp_handler.h +++ b/connections/implementation/base_pcp_handler.h @@ -149,7 +149,8 @@ class BasePcpHandler : public PcpHandler, // @EndpointManagerThread void OnEndpointDisconnect(ClientProxy* client, const std::string& service_id, const std::string& endpoint_id, - CountDownLatch barrier) override; + CountDownLatch barrier, + DisconnectionReason reason) override; Status UpdateAdvertisingOptions( ClientProxy* client, absl::string_view service_id, @@ -507,7 +508,9 @@ class BasePcpHandler : public PcpHandler, EndpointChannel* channel, bool is_incoming, absl::Time start_time, Status status, Future* result); void ProcessPreConnectionResultFailure(ClientProxy* client, - const std::string& endpoint_id); + const std::string& endpoint_id, + bool should_call_disconnect_endpoint, + const DisconnectionReason& reason); // Called when either side accepts/rejects the connection, but only takes // effect after both have accepted or one side has rejected. diff --git a/connections/implementation/base_pcp_handler_test.cc b/connections/implementation/base_pcp_handler_test.cc index ddb38e7e..59cdded1 100644 --- a/connections/implementation/base_pcp_handler_test.cc +++ b/connections/implementation/base_pcp_handler_test.cc @@ -37,6 +37,7 @@ #include "connections/implementation/client_proxy.h" #include "connections/implementation/encryption_runner.h" #include "connections/implementation/endpoint_manager.h" +#include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/mediums/mediums.h" #include "connections/implementation/offline_frames.h" #include "connections/implementation/pcp.h" @@ -48,19 +49,17 @@ #include "connections/status.h" #include "connections/strategy.h" #include "connections/v3/connection_listening_options.h" +#include "internal/flags/nearby_flags.h" #include "internal/interop/device.h" #include "internal/interop/device_provider.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/input_stream.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" #include "internal/platform/output_stream.h" #include "internal/platform/pipe.h" #include "proto/connections_enums.pb.h" -#include "proto/connections_enums.proto.h" namespace nearby { namespace connections { @@ -358,6 +357,16 @@ struct MockDiscoveredEndpoint : public MockPcpHandler::DiscoveredEndpoint { MockContext context; }; +class SetSafeToDisconnect { + public: + explicit SetSafeToDisconnect(bool safe_to_disconnect) { + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature:: + kEnableSafeToDisconnect, + safe_to_disconnect); + } +}; + class BasePcpHandlerTest : public ::testing::TestWithParam { protected: @@ -693,6 +702,7 @@ class BasePcpHandlerTest .endpoint_distance_changed_cb = mock_discovery_listener_.endpoint_distance_changed_cb.AsStdFunction(), }; + SetSafeToDisconnect set_safe_to_disconnect_{true}; MediumEnvironment& env_ = MediumEnvironment::Instance(); }; diff --git a/connections/implementation/bwu_manager.cc b/connections/implementation/bwu_manager.cc index 97f597d5..78ab4992 100644 --- a/connections/implementation/bwu_manager.cc +++ b/connections/implementation/bwu_manager.cc @@ -24,6 +24,8 @@ #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_manager.h" #include "connections/implementation/offline_frames.h" #include "connections/implementation/service_id_constants.h" #ifdef NO_WEBRTC @@ -343,7 +345,8 @@ void BwuManager::OnIncomingFrame(OfflineFrame& frame, void BwuManager::OnEndpointDisconnect(ClientProxy* client, const std::string& service_id, const std::string& endpoint_id, - CountDownLatch barrier) { + CountDownLatch barrier, + DisconnectionReason reason) { NEARBY_LOGS(INFO) << "BwuManager has processed endpoint disconnection for endpoint " << endpoint_id; @@ -1112,7 +1115,11 @@ void BwuManager::ProcessSafeToClosePriorChannelEvent( // circumstances so it is necessary to send it unencrypted. This way the // serial crypto context does not increment here. previous_endpoint_channel->DisableEncryption(); - previous_endpoint_channel->Write(parser::ForDisconnection()); + NEARBY_LOGS(INFO) << "[safe-to-disconnect] Sending " + "DISCONNECTION frame with request 0, ack 0"; + previous_endpoint_channel->Write( + parser::ForDisconnection(/* request_safe_to_disconnect */ false, + /* ack_safe_to_disconnect */ false)); // Attempt to read the disconnect message from the previous channel. We don't // care whether we successfully read it or whether we get an exception here. diff --git a/connections/implementation/bwu_manager.h b/connections/implementation/bwu_manager.h index 354564e6..c13d3208 100644 --- a/connections/implementation/bwu_manager.h +++ b/connections/implementation/bwu_manager.h @@ -94,7 +94,8 @@ class BwuManager : public EndpointManager::FrameProcessor { void OnEndpointDisconnect(ClientProxy* client_proxy, const std::string& service_id, const std::string& endpoint_id, - CountDownLatch barrier) override; + CountDownLatch barrier, + DisconnectionReason reason) override; void Shutdown(); diff --git a/connections/implementation/bwu_manager_test.cc b/connections/implementation/bwu_manager_test.cc index 037dccdd..5a5e2659 100644 --- a/connections/implementation/bwu_manager_test.cc +++ b/connections/implementation/bwu_manager_test.cc @@ -30,15 +30,19 @@ #include "connections/implementation/offline_frames.h" #include "connections/implementation/service_id_constants.h" #include "internal/platform/exception.h" +#include "internal/proto/analytics/connections_log.pb.h" +#include "proto/connections_enums.pb.h" namespace nearby { namespace connections { namespace { +using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::connections::BandwidthUpgradeNegotiationFrame; using ::location::nearby::connections:: BandwidthUpgradeNegotiationFrame_UpgradePathInfo; using ::location::nearby::connections::OfflineFrame; using ::location::nearby::connections::V1Frame; +using ::location::nearby::proto::connections::DisconnectionReason; constexpr absl::string_view kServiceIdA = "ServiceA"; constexpr absl::string_view kServiceIdB = "ServiceB"; @@ -95,6 +99,11 @@ class BwuManagerTest : public ::testing::Test { std::move(channel)); return channel_raw; } + void UnRegisterChannelForEndpoint(absl::string_view endpoint_id) { + ecm_.UnregisterChannelForEndpoint( + std::string(endpoint_id), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + } // Upgrade from |initial_medium| to |upgrade_medium|, close down the BLUETOOTH // channel, return the upgraded endpoint channel. This logic is tested in @@ -175,6 +184,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); auto channel2 = std::make_unique( Medium::BLUETOOTH, std::string(kServiceIdA)); @@ -183,6 +195,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); auto channel3 = std::make_unique( Medium::BLUETOOTH, std::string(kServiceIdA)); @@ -191,6 +206,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); auto channel4 = std::make_unique( Medium::WEB_RTC, std::string(kServiceIdA)); @@ -199,6 +217,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); bwu_manager->Shutdown(); } @@ -272,6 +293,7 @@ TEST_P(BwuManagerTestParam, InitiateBwu_Success) { EXPECT_TRUE(old_channel->is_closed()); EXPECT_EQ(location::nearby::proto::connections::DisconnectionReason::UPGRADED, old_channel->disconnection_reason()); + UnRegisterChannelForEndpoint(kEndpointId1); } TEST_P(BwuManagerTestParam, @@ -285,6 +307,7 @@ TEST_P(BwuManagerTestParam, bwu_manager_->InitiateBwuForEndpoint(&client_, std::string(kEndpointId1), Medium::WEB_RTC); EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->handle_initialize_calls().size()); + UnRegisterChannelForEndpoint(kEndpointId1); } TEST_P(BwuManagerTestParam, @@ -296,6 +319,7 @@ TEST_P(BwuManagerTestParam, Medium::WIFI_HOTSPOT); EXPECT_TRUE( fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty()); + UnRegisterChannelForEndpoint(kEndpointId1); } TEST_P(BwuManagerTestParam, InitiateBwu_Error_NoInitialMedium) { @@ -327,6 +351,7 @@ TEST_P(BwuManagerTestParam, InitiateBwu_Error_UpgradeAlreadyInProgress) { EXPECT_TRUE( fake_wifi_hotspot_bwu_handler_->handle_initialize_calls().empty()); EXPECT_TRUE(fake_wifi_direct_bwu_handler_->handle_initialize_calls().empty()); + UnRegisterChannelForEndpoint(kEndpointId1); } TEST_P(BwuManagerTestParam, @@ -358,6 +383,7 @@ TEST_P(BwuManagerTestParam, ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get()); EXPECT_EQ(initial_channel, ecm_.GetChannelForEndpoint(std::string(kEndpointId1)).get()); + UnRegisterChannelForEndpoint(kEndpointId1); } TEST_F(BwuManagerTest, @@ -380,9 +406,12 @@ TEST_F(BwuManagerTest, // Disconnect the first WebRTC endpoint. We don't expect a revert until the // last WebRTC endpoint for the service is disconnected. CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId1)); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id, - std::string(kEndpointId1), latch); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id, std::string(kEndpointId1), latch, + DisconnectionReason::LOCAL_DISCONNECTION); ASSERT_EQ(1u, fake_web_rtc_bwu_handler_->disconnect_calls().size()); EXPECT_EQ(kEndpointId1, fake_web_rtc_bwu_handler_->disconnect_calls()[0].endpoint_id); @@ -391,9 +420,12 @@ TEST_F(BwuManagerTest, { // Disconnect the second WebRTC endpoint. We expect a revert. CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId2)); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id, - std::string(kEndpointId2), latch); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id, std::string(kEndpointId2), latch, + DisconnectionReason::LOCAL_DISCONNECTION); ASSERT_EQ(2u, fake_web_rtc_bwu_handler_->disconnect_calls().size()); EXPECT_EQ(kEndpointId2, fake_web_rtc_bwu_handler_->disconnect_calls()[1].endpoint_id); @@ -423,9 +455,12 @@ TEST_F(BwuManagerTest, { // Disconnect the first WebRTC endpoint. CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId1)); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id, - std::string(kEndpointId1), latch); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id, std::string(kEndpointId1), latch, + DisconnectionReason::LOCAL_DISCONNECTION); ASSERT_EQ(1u, fake_web_rtc_bwu_handler_->disconnect_calls().size()); EXPECT_EQ(kEndpointId1, fake_web_rtc_bwu_handler_->disconnect_calls()[0].endpoint_id); @@ -440,9 +475,12 @@ TEST_F(BwuManagerTest, { // Disconnect the second WebRTC endpoint. CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId2)); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id, - std::string(kEndpointId2), latch); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id, std::string(kEndpointId2), latch, + DisconnectionReason::LOCAL_DISCONNECTION); // Note(nohle): There appears to be an off-by-one error in the // existing/flag-disabled code. Revert is called when there are "<= 1" @@ -474,10 +512,13 @@ TEST_F(BwuManagerTest, { CountDownLatch latch(1); EXPECT_EQ(2u, ecm_.GetConnectedEndpointsCount()); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId1)); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); EXPECT_EQ(1u, ecm_.GetConnectedEndpointsCount()); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id_A, - std::string(kEndpointId1), latch); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id_A, std::string(kEndpointId1), latch, + DisconnectionReason::LOCAL_DISCONNECTION); ASSERT_EQ(1u, fake_wifi_lan_bwu_handler_->disconnect_calls().size()); EXPECT_EQ(kEndpointId1, fake_wifi_lan_bwu_handler_->disconnect_calls()[0].endpoint_id); @@ -491,10 +532,13 @@ TEST_F(BwuManagerTest, } { CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId2)); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); EXPECT_EQ(0u, ecm_.GetConnectedEndpointsCount()); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id_B, - std::string(kEndpointId2), latch); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id_B, std::string(kEndpointId2), latch, + DisconnectionReason::LOCAL_DISCONNECTION); ASSERT_EQ(2u, fake_wifi_lan_bwu_handler_->disconnect_calls().size()); EXPECT_EQ(kEndpointId2, fake_wifi_lan_bwu_handler_->disconnect_calls()[1].endpoint_id); @@ -523,10 +567,13 @@ TEST_F(BwuManagerTest, { CountDownLatch latch(1); EXPECT_EQ(2u, ecm_.GetConnectedEndpointsCount()); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId1)); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); EXPECT_EQ(1u, ecm_.GetConnectedEndpointsCount()); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id_A, - std::string(kEndpointId1), latch); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id_A, std::string(kEndpointId1), latch, + DisconnectionReason::LOCAL_DISCONNECTION); ASSERT_EQ(1u, fake_wifi_lan_bwu_handler_->disconnect_calls().size()); EXPECT_EQ(kEndpointId1, fake_wifi_lan_bwu_handler_->disconnect_calls()[0].endpoint_id); @@ -540,10 +587,13 @@ TEST_F(BwuManagerTest, } { CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId2)); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); EXPECT_EQ(0u, ecm_.GetConnectedEndpointsCount()); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id_B, - std::string(kEndpointId2), latch); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id_B, std::string(kEndpointId2), latch, + DisconnectionReason::LOCAL_DISCONNECTION); // Note(nohle): There appears to be an off-by-one error in the // existing/flag-disabled code. Revert is called when there are "<= 1" // (instead of "== 0") connected endpoints. @@ -593,9 +643,12 @@ TEST_F( EXPECT_TRUE(fake_wifi_direct_bwu_handler_->handle_revert_calls().empty()); { CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId1)); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id_A, - std::string(kEndpointId1), latch); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId1), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id_A, std::string(kEndpointId1), latch, + DisconnectionReason::LOCAL_DISCONNECTION); // No more WebRTC channels for service A; expect revert call. ASSERT_EQ(1u, fake_web_rtc_bwu_handler_->disconnect_calls().size()); @@ -615,9 +668,12 @@ TEST_F( } { CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId2)); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id_A, - std::string(kEndpointId2), latch); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId2), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id_A, std::string(kEndpointId2), latch, + DisconnectionReason::LOCAL_DISCONNECTION); // We reverted a WLAN channel; no additional WebRTC calls expected. EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->disconnect_calls().size()); @@ -633,9 +689,12 @@ TEST_F( } { CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId3)); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id_B, - std::string(kEndpointId3), latch); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId3), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id_B, std::string(kEndpointId3), latch, + DisconnectionReason::LOCAL_DISCONNECTION); // We reverted a WLAN channel; no additional WebRTC calls expected. EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->disconnect_calls().size()); @@ -651,9 +710,12 @@ TEST_F( } { CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId4)); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id_B, - std::string(kEndpointId4), latch); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId4), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id_B, std::string(kEndpointId4), latch, + DisconnectionReason::LOCAL_DISCONNECTION); // We reverted a Hotspot channel; no additional WebRTC calls expected. EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->disconnect_calls().size()); @@ -671,9 +733,12 @@ TEST_F( } { CountDownLatch latch(1); - ecm_.UnregisterChannelForEndpoint(std::string(kEndpointId5)); - bwu_manager_->OnEndpointDisconnect(&client_, upgrade_service_id_B, - std::string(kEndpointId5), latch); + ecm_.UnregisterChannelForEndpoint( + std::string(kEndpointId5), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION); + bwu_manager_->OnEndpointDisconnect( + &client_, upgrade_service_id_B, std::string(kEndpointId5), latch, + DisconnectionReason::LOCAL_DISCONNECTION); // We reverted a WifiDirect channel; no additional WebRTC calls expected. EXPECT_EQ(1u, fake_web_rtc_bwu_handler_->disconnect_calls().size()); @@ -722,6 +787,9 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnUpgradeFailure_FlagEnabled) { ASSERT_EQ(1u, fake_web_rtc_bwu_handler_->handle_revert_calls().size()); EXPECT_EQ(WrapInitiatorUpgradeServiceId(kServiceIdB), fake_web_rtc_bwu_handler_->handle_revert_calls()[0].service_id); + UnRegisterChannelForEndpoint(kEndpointId1); + UnRegisterChannelForEndpoint(kEndpointId2); + UnRegisterChannelForEndpoint(kEndpointId3); } TEST_F(BwuManagerTest, InitiateBwu_Revert_OnUpgradeFailure_FlagDisabled) { @@ -756,6 +824,9 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnUpgradeFailure_FlagDisabled) { // endpoints for _any_ service. We don't have service-level bookkeeping; we // only know that there is some active WebRTC endpoint. EXPECT_TRUE(fake_web_rtc_bwu_handler_->handle_revert_calls().empty()); + UnRegisterChannelForEndpoint(kEndpointId1); + UnRegisterChannelForEndpoint(kEndpointId2); + UnRegisterChannelForEndpoint(kEndpointId3); } TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_WifiDirect) { @@ -779,7 +850,8 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_WifiDirect) { Medium::BLUETOOTH, packet_meta_data_); CountDownLatch latch(1); bwu_manager_->OnEndpointDisconnect(&client_, (std::string)kServiceIdA, - std::string(kEndpointId1), latch); + std::string(kEndpointId1), latch, + DisconnectionReason::LOCAL_DISCONNECTION); ASSERT_EQ(fake_wifi_direct_bwu_handler_->disconnect_calls().size(), 1u); EXPECT_EQ(kEndpointId1, @@ -787,6 +859,7 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_WifiDirect) { // This is called by the RESPONDER--call RevertInitiatorState only when // BWU Medium is Hotspot or WifiDirect. ASSERT_EQ(fake_wifi_direct_bwu_handler_->handle_revert_calls().size(), 1u); + UnRegisterChannelForEndpoint(kEndpointId1); } TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_Hotspot) { @@ -811,9 +884,11 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_Hotspot) { Medium::BLUETOOTH, packet_meta_data_); CountDownLatch latch(1); bwu_manager_->OnEndpointDisconnect(&client_, (std::string)kServiceIdA, - std::string(kEndpointId1), latch); + std::string(kEndpointId1), latch, + DisconnectionReason::LOCAL_DISCONNECTION); ASSERT_EQ(fake_wifi_hotspot_bwu_handler_->handle_revert_calls().size(), 1u); + UnRegisterChannelForEndpoint(kEndpointId1); } TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_Wlan) { @@ -837,9 +912,11 @@ TEST_F(BwuManagerTest, InitiateBwu_Revert_OnDisconnect_Wlan) { Medium::BLUETOOTH, packet_meta_data_); CountDownLatch latch(1); bwu_manager_->OnEndpointDisconnect(&client_, (std::string)kServiceIdA, - std::string(kEndpointId1), latch); + std::string(kEndpointId1), latch, + DisconnectionReason::LOCAL_DISCONNECTION); ASSERT_EQ(fake_wifi_lan_bwu_handler_->handle_revert_calls().size(), 0u); + UnRegisterChannelForEndpoint(kEndpointId1); } TEST_F(BwuManagerTest, OnReceiveBwuEvent) { diff --git a/connections/implementation/client_proxy.cc b/connections/implementation/client_proxy.cc index c3a2e6fe..06768e72 100644 --- a/connections/implementation/client_proxy.cc +++ b/connections/implementation/client_proxy.cc @@ -14,6 +14,7 @@ #include "connections/implementation/client_proxy.h" +#include #include #include #include @@ -28,11 +29,13 @@ #include "absl/container/flat_hash_set.h" #include "absl/functional/any_invocable.h" #include "absl/strings/escaping.h" -#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/v3/bandwidth_info.h" #include "connections/v3/connection_listening_options.h" #include "connections/v3/connections_device_provider.h" #include "internal/analytics/event_logger.h" +#include "internal/flags/nearby_flags.h" #include "internal/platform/error_code_recorder.h" #include "internal/platform/feature_flags.h" #include "internal/platform/implementation/platform.h" @@ -67,6 +70,12 @@ ClientProxy::ClientProxy(::nearby::analytics::EventLogger* event_logger) }); local_os_info_.set_type( OSNameToOsInfoType(api::ImplementationPlatform::GetCurrentOS())); + supports_safe_to_disconnect_ = NearbyFlags::GetInstance().GetBoolFlag( + config_package_nearby::nearby_connections_feature:: + kEnableSafeToDisconnect); + local_safe_to_disconnect_version_ = NearbyFlags::GetInstance().GetInt64Flag( + config_package_nearby::nearby_connections_feature:: + kSafeToDisconnectVersion); } ClientProxy::~ClientProxy() { Reset(); } @@ -807,6 +816,44 @@ void ClientProxy::SetRemoteOsInfo(absl::string_view endpoint_id, item->first.os_info.emplace(remote_os_info); } } + +std::optional ClientProxy::GetRemoteSafeToDisconnectVersion( + absl::string_view endpoint_id) const { + const ConnectionPair* item = LookupConnection(endpoint_id); + if (item != nullptr) { + return item->first.safe_to_disconnect_version; + } + return std::nullopt; +} + +void ClientProxy::SetRemoteSafeToDisconnectVersion( + absl::string_view endpoint_id, + const std::int32_t& safe_to_disconnect_version) { + ConnectionPair* item = LookupConnection(endpoint_id); + if (item != nullptr) { + item->first.safe_to_disconnect_version = safe_to_disconnect_version; + } +} + +bool ClientProxy::IsSafeToDisconnectEnabled(absl::string_view endpoint_id) { + return IsSupportSafeToDisconnect() && + GetRemoteSafeToDisconnectVersion(endpoint_id).has_value() && + (GetRemoteSafeToDisconnectVersion(endpoint_id) >= + FeatureFlags::GetInstance() + .GetFlags() + .min_nc_version_supports_safe_to_disconnect); +} + +bool ClientProxy::IsPayloadReceivedAckEnabled(absl::string_view endpoint_id) { + return IsSupportSafeToDisconnect() && + GetRemoteSafeToDisconnectVersion(endpoint_id).has_value() && + (GetRemoteSafeToDisconnectVersion(endpoint_id) >= + FeatureFlags::GetInstance() + .GetFlags() + .min_nc_version_supports_payload_received_ack); +} + + void ClientProxy::CancelAllEndpoints() { for (const auto& item : cancellation_flags_) { CancellationFlag* cancellation_flag = item.second.get(); diff --git a/connections/implementation/client_proxy.h b/connections/implementation/client_proxy.h index f19190e4..e39ae058 100644 --- a/connections/implementation/client_proxy.h +++ b/connections/implementation/client_proxy.h @@ -267,6 +267,20 @@ class ClientProxy final { connections_device_provider_ = std::move(provider); } + const bool& IsSupportSafeToDisconnect() const { + return supports_safe_to_disconnect_; + } + 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 IsPayloadReceivedAckEnabled(absl::string_view endpoint_id); + private: struct Connection { // Status: may be either: @@ -296,6 +310,7 @@ class ClientProxy final { AdvertisingOptions advertising_options; std::string connection_token; std::optional os_info; + std::int32_t safe_to_disconnect_version; }; using ConnectionPair = std::pair; @@ -427,6 +442,8 @@ class ClientProxy final { NearbyDeviceProvider* external_device_provider_ = nullptr; // For Nearby Connections' own device provider. std::unique_ptr connections_device_provider_; + bool supports_safe_to_disconnect_; + std::int32_t local_safe_to_disconnect_version_; }; } // namespace connections diff --git a/connections/implementation/client_proxy_test.cc b/connections/implementation/client_proxy_test.cc index 011cb8ec..3d53bc87 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 @@ -40,7 +41,7 @@ #include "internal/platform/count_down_latch.h" #include "internal/platform/feature_flags.h" #include "internal/platform/medium_environment.h" -#include "proto/connections_enums.proto.h" +#include "proto/connections_enums.pb.h" namespace nearby { namespace connections { @@ -1023,6 +1024,9 @@ TEST_F(ClientProxyTest, GetRemoteInfoNullWithoutConnections) { StartAdvertising(&client1_, advertising_connection_listener_); EXPECT_FALSE(client1_.GetRemoteOsInfo(advertising_endpoint.id).has_value()); + EXPECT_FALSE( + client1_.GetRemoteSafeToDisconnectVersion(advertising_endpoint.id) + .has_value()); } TEST_F(ClientProxyTest, SetRemoteInfoCorrect) { @@ -1032,11 +1036,16 @@ TEST_F(ClientProxyTest, SetRemoteInfoCorrect) { OsInfo os_info; os_info.set_type(OsInfo::ANDROID); + std::int32_t nearby_connections_version = 2; client1_.SetRemoteOsInfo(advertising_endpoint.id, os_info); + client1_.SetRemoteSafeToDisconnectVersion(advertising_endpoint.id, + nearby_connections_version); ASSERT_TRUE(client1_.GetRemoteOsInfo(advertising_endpoint.id).has_value()); EXPECT_EQ(client1_.GetRemoteOsInfo(advertising_endpoint.id).value().type(), OsInfo::ANDROID); + EXPECT_EQ(client1_.GetRemoteSafeToDisconnectVersion(advertising_endpoint.id), + nearby_connections_version); } // Test ClientProxy::AddCancellationFlag, where if a flag is already in the map, diff --git a/connections/implementation/endpoint_channel_manager.cc b/connections/implementation/endpoint_channel_manager.cc index f8c66e21..cc3b5282 100644 --- a/connections/implementation/endpoint_channel_manager.cc +++ b/connections/implementation/endpoint_channel_manager.cc @@ -20,12 +20,16 @@ #include "absl/time/time.h" #include "connections/implementation/offline_frames.h" +#include "internal/platform/condition_variable.h" +#include "internal/platform/feature_flags.h" #include "internal/platform/logging.h" #include "internal/platform/mutex.h" #include "internal/platform/mutex_lock.h" +#include "proto/connections_enums.pb.h" namespace nearby { namespace connections { +using ::location::nearby::analytics::proto::ConnectionsLog; namespace { const absl::Duration kDataTransferDelay = absl::Milliseconds(500); @@ -97,7 +101,8 @@ void EndpointChannelManager::SetActiveEndpointChannel( // crypto context is present. channel->SetAnalyticsRecorder(&client->GetAnalyticsRecorder(), endpoint_id); channel_state_.UpdateChannelForEndpoint(endpoint_id, std::move(channel)); - + channel_state_.UpdateSafeToDisconnectForEndpoint( + endpoint_id, client->IsSafeToDisconnectEnabled(endpoint_id)); auto* endpoint = channel_state_.LookupEndpointData(endpoint_id); if (endpoint->IsEncrypted() && enable_encryption) channel_state_.EncryptChannel(endpoint); @@ -113,6 +118,37 @@ bool EndpointChannelManager::isWifiLanConnected() const { return channel_state_.isWifiLanConnected(); } +void EndpointChannelManager::UpdateSafeToDisconnectForEndpoint( + const std::string& endpoint_id, + bool safe_to_disconnect_enabled) { + MutexLock lock(&mutex_); + channel_state_.UpdateSafeToDisconnectForEndpoint(endpoint_id, + safe_to_disconnect_enabled); +} + +void EndpointChannelManager::MarkEndpointStopWaitToDisconnect( + const std::string& endpoint_id, bool is_safe_to_disconnect, + bool notify_stop_waiting) { + MutexLock lock(&mutex_); + channel_state_.MarkEndpointStopWaitToDisconnect( + endpoint_id, is_safe_to_disconnect, notify_stop_waiting); +} + +bool EndpointChannelManager::CreateNewTimeoutDisconnectedState( + const std::string& endpoint_id) { + return channel_state_.CreateNewTimeoutDisconnectedState(endpoint_id); +} + +bool EndpointChannelManager::IsSafeToDisconnect( + const std::string& endpoint_id) { + return channel_state_.IsSafeToDisconnect(endpoint_id); +} +void EndpointChannelManager::RemoveTimeoutDisconnectedState( + const std::string& endpoint_id) { + MutexLock lock(&mutex_); + channel_state_.RemoveTimeoutDisconnectedState(endpoint_id); +} + ///////////////////////////////// ChannelState ///////////////////////////////// // endpoint - channel endpoint to encrypt @@ -133,6 +169,15 @@ EndpointChannelManager::ChannelState::LookupEndpointData( return item != endpoints_.end() ? &item->second : nullptr; } +void EndpointChannelManager::ChannelState::DestroyAll() { + for (auto& item : endpoints_) { + RemoveEndpoint(item.first, DisconnectionReason::SHUTDOWN, + /* safe_to_disconnect_enabled */ false, + ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + } + endpoints_.clear(); +} + void EndpointChannelManager::ChannelState::UpdateChannelForEndpoint( const std::string& endpoint_id, std::unique_ptr channel) { // Create EndpointData instance, if necessary, and populate channel. @@ -146,24 +191,54 @@ void EndpointChannelManager::ChannelState::UpdateEncryptionContextForEndpoint( endpoints_[endpoint_id].context = std::move(context); } -bool EndpointChannelManager::ChannelState::RemoveEndpoint( +void EndpointChannelManager::ChannelState::UpdateSafeToDisconnectForEndpoint( const std::string& endpoint_id, - location::nearby::proto::connections::DisconnectionReason reason) { + bool safe_to_disconnect_enabled) { + NEARBY_LOGS(INFO) << "[safe-to-disconnect] " + "UpdateSafeToDisconnectForEndpoint for: " + << endpoint_id << " " << safe_to_disconnect_enabled; + + endpoints_[endpoint_id].safe_to_disconnect_enabled = + safe_to_disconnect_enabled; +} + +bool EndpointChannelManager::ChannelState::GetSafeToDisconnectForEndpoint( + const std::string& endpoint_id) { auto item = endpoints_.find(endpoint_id); if (item == endpoints_.end()) return false; + NEARBY_LOGS(INFO) << "[safe-to-disconnect] GetSafeToDisconnectForEndpoint: " + << item->second.safe_to_disconnect_enabled; + return item->second.safe_to_disconnect_enabled; +} + +bool EndpointChannelManager::ChannelState::RemoveEndpoint( + const std::string& endpoint_id, DisconnectionReason reason, + bool safe_to_disconnect_enabled, SafeDisconnectionResult result) { + auto item = endpoints_.find(endpoint_id); + if (item == endpoints_.end()) return false; + + MarkEndpointStopWaitToDisconnect(endpoint_id, + /* is_safe_to_disconnect */ true, + /* notify_stop_waiting */ true); item->second.disconnect_reason = reason; auto channel = item->second.channel; - if (channel) { + + if (channel && !safe_to_disconnect_enabled) { // If the channel was paused (i.e. during a bandwidth upgrade negotiation) // we resume to ensure the thread won't hang when trying to write to it. channel->Resume(); - channel->Write(parser::ForDisconnection()); + NEARBY_LOGS(INFO) << "[safe-to-disconnect] Sending DISCONNECTION frame" + " with request 0, ack 0"; + channel->Write( + parser::ForDisconnection(/* request_safe_to_disconnect */ false, + /* ack_safe_to_disconnect */ false)); NEARBY_LOGS(INFO) << "EndpointChannelManager reported the disconnection to endpoint " << endpoint_id; SystemClock::Sleep(kDataTransferDelay); } + NEARBY_LOGS(INFO) << "Remove Endpoint: " << endpoint_id; endpoints_.erase(item); return true; } @@ -183,20 +258,93 @@ bool EndpointChannelManager::ChannelState::isWifiLanConnected() const { return false; } -bool EndpointChannelManager::UnregisterChannelForEndpoint( +void EndpointChannelManager::ChannelState::MarkEndpointStopWaitToDisconnect( + const std::string& endpoint_id, bool is_safe_to_disconnect, + bool notify_stop_waiting) { + auto item = endpoints_.find(endpoint_id); + if (item == endpoints_.end()) return; + NEARBY_LOGS(INFO) << "[safe-to-disconnect] is_safe_to_disconnect= " + << is_safe_to_disconnect + << ", notify_stop_waiting= " << notify_stop_waiting + << " for endpoint: " << endpoint_id; + { + MutexLock lock(&item->second.timeout_to_disconnected_mutex); + item->second.is_safe_to_disconnect = is_safe_to_disconnect; + if (!item->second.timeout_to_disconnected_enabled) return; + if (notify_stop_waiting) { + NEARBY_LOGS(INFO) << "[safe-to-disconnect] Notify stop " + "waiting before timeout."; + item->second.timeout_to_disconnected.Notify(); + item->second.timeout_to_disconnected_notified = true; + } + } +} + +bool EndpointChannelManager::ChannelState::CreateNewTimeoutDisconnectedState( const std::string& endpoint_id) { + auto item = endpoints_.find(endpoint_id); + if (item == endpoints_.end()) return false; + NEARBY_LOGS(INFO) << "[safe-to-disconnect] " + "Create TimeoutDisconnectedState for endpoint: " + << endpoint_id; + { + MutexLock lock(&item->second.timeout_to_disconnected_mutex); + item->second.timeout_to_disconnected_enabled = true; + item->second.timeout_to_disconnected_notified = false; + item->second.timeout_to_disconnected.Wait(FeatureFlags::GetInstance() + .GetFlags() + .safe_to_disconnect_ack_delay_millis); + NEARBY_LOGS(INFO) << "[safe-to-disconnect] Wait is done with " + << (item->second.timeout_to_disconnected_notified + ? "notification" + : "timeout"); + if (!item->second.timeout_to_disconnected_notified) + item->second.is_safe_to_disconnect = true; + item->second.timeout_to_disconnected_notified = false; + item->second.timeout_to_disconnected_enabled = false; + } + return true; +} + +bool EndpointChannelManager::ChannelState::IsSafeToDisconnect( + const std::string& endpoint_id) { + + auto item = endpoints_.find(endpoint_id); + if (item == endpoints_.end()) return true; + { + MutexLock lock(&item->second.timeout_to_disconnected_mutex); + NEARBY_LOGS(INFO) + << "[safe-to-disconnect] Get SafeToDisconnect status for endpoint: " + << endpoint_id << ": " << item->second.is_safe_to_disconnect; + return (item->second.is_safe_to_disconnect); + } +} + +void EndpointChannelManager::ChannelState::RemoveTimeoutDisconnectedState( + const std::string& endpoint_id) { + auto item = endpoints_.find(endpoint_id); + if (item == endpoints_.end()) return; + { + MutexLock lock(&item->second.timeout_to_disconnected_mutex); + item->second.timeout_to_disconnected_notified = false; + item->second.timeout_to_disconnected_enabled = false; + } +} + +bool EndpointChannelManager::UnregisterChannelForEndpoint( + const std::string& endpoint_id, DisconnectionReason reason, + SafeDisconnectionResult result) { MutexLock lock(&mutex_); - if (!channel_state_.RemoveEndpoint( - endpoint_id, location::nearby::proto::connections:: - DisconnectionReason::LOCAL_DISCONNECTION)) { + auto safe_to_disconnect_enabled = + channel_state_.GetSafeToDisconnectForEndpoint(endpoint_id); + if (!channel_state_.RemoveEndpoint(endpoint_id, reason, + safe_to_disconnect_enabled, result)) { return false; } - NEARBY_LOGS(INFO) << "EndpointChannelManager unregistered channel for endpoint " << endpoint_id; - return true; } diff --git a/connections/implementation/endpoint_channel_manager.h b/connections/implementation/endpoint_channel_manager.h index 980c53a7..16980164 100644 --- a/connections/implementation/endpoint_channel_manager.h +++ b/connections/implementation/endpoint_channel_manager.h @@ -17,15 +17,23 @@ #include #include +#include #include "securegcm/d2d_connection_context_v1.h" +#include "absl/base/thread_annotations.h" #include "absl/container/flat_hash_map.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" +#include "internal/platform/feature_flags.h" #include "internal/platform/mutex.h" +#include "internal/proto/analytics/connections_log.pb.h" namespace nearby { namespace connections { +using DisconnectionReason = + ::location::nearby::proto::connections::DisconnectionReason; +using SafeDisconnectionResult = ::location::nearby::analytics::proto:: + ConnectionsLog::EstablishedConnection::SafeDisconnectionResult; // NOTE(std::string): // All the strings in internal class public interfaces should be exchanged as @@ -90,13 +98,28 @@ class EndpointChannelManager final { // Returns true if 'endpoint_id' actually had a registered EndpointChannel. // IOW, a return of false signifies a no-op. - bool UnregisterChannelForEndpoint(const std::string& endpoint_id) + bool UnregisterChannelForEndpoint(const std::string& endpoint_id, + DisconnectionReason reason, + SafeDisconnectionResult result) ABSL_LOCKS_EXCLUDED(mutex_); int GetConnectedEndpointsCount() const ABSL_LOCKS_EXCLUDED(mutex_); // Check if any endpoint uses WLAN Medium bool isWifiLanConnected() const ABSL_LOCKS_EXCLUDED(mutex_); + void UpdateSafeToDisconnectForEndpoint(const std::string& endpoint_id, + bool safe_to_disconnect_enabled) + ABSL_LOCKS_EXCLUDED(mutex_); + void MarkEndpointStopWaitToDisconnect(const std::string& endpoint_id, + bool is_safe_to_disconnect, + bool notify_stop_waiting) + ABSL_LOCKS_EXCLUDED(mutex_); + bool CreateNewTimeoutDisconnectedState(const std::string& endpoint_id) + ABSL_LOCKS_EXCLUDED(mutex_); + bool IsSafeToDisconnect(const std::string& endpoint_id) + ABSL_LOCKS_EXCLUDED(mutex_); + void RemoveTimeoutDisconnectedState(const std::string& endpoint_id) + ABSL_LOCKS_EXCLUDED(mutex_); private: // Tracks channel state for all endpoints. This includes what EndpointChannel @@ -119,9 +142,17 @@ class EndpointChannelManager final { std::shared_ptr channel; std::shared_ptr context; - location::nearby::proto::connections::DisconnectionReason - disconnect_reason = location::nearby::proto::connections:: - DisconnectionReason::UNKNOWN_DISCONNECTION_REASON; + DisconnectionReason disconnect_reason = + DisconnectionReason::UNKNOWN_DISCONNECTION_REASON; + bool safe_to_disconnect_enabled = false; + mutable Mutex timeout_to_disconnected_mutex; + ConditionVariable timeout_to_disconnected{&timeout_to_disconnected_mutex}; + bool timeout_to_disconnected_enabled + ABSL_GUARDED_BY(timeout_to_disconnected_mutex) = false; + bool timeout_to_disconnected_notified + ABSL_GUARDED_BY(timeout_to_disconnected_mutex) = false; + bool is_safe_to_disconnect + ABSL_GUARDED_BY(timeout_to_disconnected_mutex) = false; }; ChannelState() = default; @@ -130,7 +161,7 @@ class EndpointChannelManager final { ChannelState& operator=(ChannelState&&) = default; // Provides a way to destroy contents of a container, while holding a lock. - void DestroyAll() { endpoints_.clear(); } + void DestroyAll(); // Return pointer to endpoint data, or nullptr, it not found. EndpointData* LookupEndpointData(const std::string& endpoint_id); @@ -145,15 +176,26 @@ class EndpointChannelManager final { const std::string& endpoint_id, std::unique_ptr context); + void UpdateSafeToDisconnectForEndpoint(const std::string& endpoint_id, + bool safe_to_disconnect_enabled); + bool GetSafeToDisconnectForEndpoint(const std::string& endpoint_id); + // Removes all knowledge of this endpoint, cleaning up as necessary. // Returns false if the endpoint was not found. - bool RemoveEndpoint( - const std::string& endpoint_id, - location::nearby::proto::connections::DisconnectionReason reason); + bool RemoveEndpoint(const std::string& endpoint_id, + DisconnectionReason reason, + bool safe_to_disconnect_enabled, + SafeDisconnectionResult result); bool EncryptChannel(EndpointData* endpoint); int GetConnectedEndpointsCount() const { return endpoints_.size(); } bool isWifiLanConnected() const; + void MarkEndpointStopWaitToDisconnect(const std::string& endpoint_id, + bool is_safe_to_disconnect, + bool notify_stop_waiting); + bool CreateNewTimeoutDisconnectedState(const std::string& endpoint_id); + bool IsSafeToDisconnect(const std::string& endpoint_id); + void RemoveTimeoutDisconnectedState(const std::string& endpoint_id); private: // Endpoint ID -> EndpointData. Contains everything we know about the @@ -168,7 +210,7 @@ class EndpointChannelManager final { ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); mutable Mutex mutex_; - ChannelState channel_state_ ABSL_GUARDED_BY(mutex_); + ChannelState channel_state_; }; } // namespace connections diff --git a/connections/implementation/endpoint_channel_manager_test.cc b/connections/implementation/endpoint_channel_manager_test.cc index 466f67c0..eb0da0c2 100644 --- a/connections/implementation/endpoint_channel_manager_test.cc +++ b/connections/implementation/endpoint_channel_manager_test.cc @@ -39,12 +39,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 { +using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::proto::connections::DisconnectionReason; using ::location::nearby::proto::connections::Medium; using EncryptionContext = BaseEndpointChannel::EncryptionContext; @@ -240,6 +242,12 @@ TEST(BaseEndpointChannelManagerTest, RegisterChannelEncryptedReadwrite) { // Shutdown test environment. channel_a_raw->Close(DisconnectionReason::LOCAL_DISCONNECTION); channel_b_raw->Close(DisconnectionReason::REMOTE_DISCONNECTION); + ecm_a.UnregisterChannelForEndpoint( + std::string(kEndpointId), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + ecm_b.UnregisterChannelForEndpoint( + std::string(kEndpointId), DisconnectionReason::REMOTE_DISCONNECTION, + ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); } TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) { @@ -302,7 +310,12 @@ TEST(BaseEndpointChannelManagerTest, ReplaceChannelNoEncrypted) { // Shutdown test environment. channel_a_raw->Close(DisconnectionReason::LOCAL_DISCONNECTION); channel_b_raw->Close(DisconnectionReason::REMOTE_DISCONNECTION); -} + ecm_a.UnregisterChannelForEndpoint( + std::string(kEndpointId), DisconnectionReason::LOCAL_DISCONNECTION, + ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); + ecm_b.UnregisterChannelForEndpoint( + std::string(kEndpointId), DisconnectionReason::REMOTE_DISCONNECTION, + ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION);} } // namespace } // namespace connections diff --git a/connections/implementation/endpoint_manager.cc b/connections/implementation/endpoint_manager.cc index 4a9f5a32..1cccfc41 100644 --- a/connections/implementation/endpoint_manager.cc +++ b/connections/implementation/endpoint_manager.cc @@ -21,8 +21,11 @@ #include #include +#include "absl/time/time.h" #include "connections/implementation/analytics/throughput_recorder.h" +#include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel.h" +#include "connections/implementation/endpoint_channel_manager.h" #include "connections/implementation/offline_frames.h" #include "connections/implementation/payload_manager.h" #include "connections/implementation/proto/offline_wire_formats.pb.h" @@ -30,12 +33,16 @@ #include "internal/platform/count_down_latch.h" #include "internal/platform/exception.h" #include "internal/platform/logging.h" +#include "internal/platform/mutex.h" #include "internal/platform/mutex_lock.h" +#include "internal/proto/analytics/connections_log.pb.h" +#include "proto/connections_enums.pb.h" namespace nearby { namespace connections { namespace { +using ::location::nearby::analytics::proto::ConnectionsLog; using ::location::nearby::connections::OfflineFrame; using ::location::nearby::connections::V1Frame; using ::nearby::analytics::PacketMetaData; @@ -162,6 +169,11 @@ void EndpointManager::EndpointChannelLoopRunnable( NEARBY_LOGS(INFO) << "Dropping current channel: last medium=" << location::nearby::proto::connections::Medium_Name( last_failed_medium); + if (client->IsSafeToDisconnectEnabled(endpoint_id)) { + channel_manager_->MarkEndpointStopWaitToDisconnect( + endpoint_id, /* is_safe_to_disconnect */ false, + /* notify_stop_waiting */ true); + } break; } } @@ -171,7 +183,7 @@ void EndpointManager::EndpointChannelLoopRunnable( << "; endpoint_id=" << endpoint_id; // Always clear out all state related to this endpoint before terminating // this thread. - DiscardEndpoint(client, endpoint_id); + DiscardEndpoint(client, endpoint_id, DisconnectionReason::IO_ERROR); NEARBY_LOGS(INFO) << "Worker done; worker name=" << runnable_name << "; endpoint_id=" << endpoint_id; } @@ -260,7 +272,7 @@ ExceptionOr EndpointManager::HandleData( } else if (frame_type == V1Frame::DISCONNECTION) { NEARBY_LOG(INFO, "Disconnect message for endpoint %s", endpoint_id.c_str()); - endpoint_channel->Close(); + ProcessDisconnectionFrame(client, endpoint_id, endpoint_channel, frame); } else { NEARBY_LOGS(ERROR) << "Unhandled message: endpoint_id=" << endpoint_id << ", frame type=" @@ -275,6 +287,62 @@ ExceptionOr EndpointManager::HandleData( } } +void EndpointManager::ProcessDisconnectionFrame( + ClientProxy* client, const std::string& endpoint_id, + EndpointChannel* endpoint_channel, OfflineFrame& frame) { + if (!client->IsSafeToDisconnectEnabled(endpoint_id)) { + NEARBY_LOGS(INFO) + << "EndpointManager received a DISCONNECTION frame from endpoint " + << endpoint_id << " on channel " << endpoint_channel->GetType() + << ", disconnecting..."; + endpoint_channel->Close(DisconnectionReason::REMOTE_DISCONNECTION); + return; + } + + if (!frame.v1().has_disconnection() || + !frame.v1().disconnection().has_request_safe_to_disconnect() || + !frame.v1().disconnection().request_safe_to_disconnect()) { + NEARBY_LOGS(INFO) << "[safe-to-disconnect] no need to apply " + "safe-to-disconnect protocol for endpoint " + << endpoint_id << " on channel " + << endpoint_channel->GetType() << ", disconnecting..."; + endpoint_channel->Close(DisconnectionReason::REMOTE_DISCONNECTION); + return; + } + NEARBY_LOGS(INFO) + << "[safe-to-disconnect] received a " + "DISCONNECTION frame with request safe to disconnect = true and ack = " + << frame.v1().disconnection().ack_safe_to_disconnect() + << " from endpoint " << endpoint_id << " on channel " + << endpoint_channel->GetType() + << ", disconnecting with safe-to-disconnect protocol ..."; + if (frame.v1().disconnection().ack_safe_to_disconnect()) { + channel_manager_->MarkEndpointStopWaitToDisconnect( + endpoint_id, /* is_safe_to_disconnect */ true, + /* notify_stop_waiting */ true); + } else { + channel_manager_->MarkEndpointStopWaitToDisconnect( + endpoint_id, /* is_safe_to_disconnect */ true, + /* notify_stop_waiting */ false); + RunOnEndpointManagerThread( + "safe-to-disconnect", [this, client, &endpoint_id]() { + RemoveEndpoint(client, endpoint_id, /*notify=*/true, + DisconnectionReason::REMOTE_DISCONNECTION); + }); + endpoint_channel->Resume(); + NEARBY_LOGS(INFO) << "[safe-to-disconnect] Sending " + "DISCONNECTION frame with request 1, ack 1"; + Exception write_exception = endpoint_channel->Write( + parser::ForDisconnection(/* request_safe_to_disconnect= */ true, + /* ack_safe_to_disconnect= */ true)); + if (!write_exception.Ok()) { + NEARBY_LOGS(INFO) << "[safe-to-disconnect] Failed to send " + "DISCONNECTION frame with ack to endpoint" + << endpoint_id; + } + } +} + ExceptionOr EndpointManager::HandleKeepAlive( EndpointChannel* endpoint_channel, absl::Duration keep_alive_interval, absl::Duration keep_alive_timeout, Mutex* keep_alive_waiter_mutex, @@ -290,10 +358,10 @@ ExceptionOr EndpointManager::HandleKeepAlive( return ExceptionOr(false); } - // If we haven't written anything to the endpoint for a while, attempt to send - // the KeepAlive frame over the endpoint channel. If the write fails, our - // super class will loop back around and try our luck again in case there's - // been a replacement for this endpoint. + // If we haven't written anything to the endpoint for a while, attempt to + // send the KeepAlive frame over the endpoint channel. If the write fails, + // our super class will loop back around and try our luck again in case + // there's been a replacement for this endpoint. absl::Time last_write_time = endpoint_channel->GetLastWriteTimestamp(); absl::Duration duration_until_write_keep_alive = last_write_time == kInvalidTimestamp @@ -323,15 +391,15 @@ ExceptionOr EndpointManager::HandleKeepAlive( bool operator==(const EndpointManager::FrameProcessor& lhs, const EndpointManager::FrameProcessor& rhs) { - // We're comparing addresses because these objects are callbacks which need to - // be matched by exact instances. + // We're comparing addresses because these objects are callbacks which need + // to be matched by exact instances. return &lhs == &rhs; } bool operator<(const EndpointManager::FrameProcessor& lhs, const EndpointManager::FrameProcessor& rhs) { - // We're comparing addresses because these objects are callbacks which need to - // be matched by exact instances. + // We're comparing addresses because these objects are callbacks which need + // to be matched by exact instances. return &lhs < &rhs; } @@ -444,10 +512,11 @@ void EndpointManager::RegisterEndpoint( // 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. + // 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, @@ -456,8 +525,8 @@ void EndpointManager::RegisterEndpoint( &latch]() { if (endpoints_.contains(endpoint_id)) { NEARBY_LOGS(WARNING) << "Registering duplicate endpoint " << endpoint_id; - // We must remove old endpoint state before registering a new one for the - // same endpoint_id. + // We must remove old endpoint state before registering a new one + // for the same endpoint_id. RemoveEndpointState(endpoint_id); } @@ -487,8 +556,8 @@ void EndpointManager::RegisterEndpoint( // 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 + // 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]() { @@ -499,17 +568,18 @@ void EndpointManager::RegisterEndpoint( }); }); - // 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. + // 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. + // 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. NEARBY_LOGS(VERBOSE) << "EndpointManager enabling KeepAlive for endpoint " << endpoint_id; endpoint_state.StartEndpointKeepAliveManager( @@ -545,7 +615,8 @@ void EndpointManager::UnregisterEndpoint(ClientProxy* client, RunOnEndpointManagerThread( "unregister-endpoint", [this, client, endpoint_id, &latch]() { RemoveEndpoint(client, endpoint_id, - /*notify=*/client->IsConnectedToEndpoint(endpoint_id)); + /*notify=*/client->IsConnectedToEndpoint(endpoint_id), + DisconnectionReason::LOCAL_DISCONNECTION); latch.CountDown(); }); latch.Await(); @@ -578,12 +649,19 @@ std::vector EndpointManager::SendPayloadChunk( } // Designed to run asynchronously. It is called from IO thread pools, and -// jobs in these pools may be waited for from the EndpointManager thread. If we -// allow synchronous behavior here it will cause a live lock. +// jobs in these pools may be waited for from the EndpointManager thread. If +// we allow synchronous behavior here it will cause a live lock. void EndpointManager::DiscardEndpoint(ClientProxy* client, - const std::string& endpoint_id) { - NEARBY_LOGS(VERBOSE) << "DiscardEndpoint for endpoint " << endpoint_id; - RunOnEndpointManagerThread("discard-endpoint", [this, client, endpoint_id]() { + const std::string& endpoint_id, + DisconnectionReason reason) { + NEARBY_LOGS(INFO) << "DiscardEndpoint for endpoint " << endpoint_id; + if (reason == DisconnectionReason::IO_ERROR) { + channel_manager_->MarkEndpointStopWaitToDisconnect( + endpoint_id, /* is_safe_to_disconnect */ false, + /* notify_stop_waiting */ true); + } + RunOnEndpointManagerThread("discard-endpoint", [this, client, endpoint_id, + reason]() { // `ClientProxy` is destroyed before `EndpointManager` in // `~NearbyConnections`, which means "discard-endpoint" needs to check // if this task is being executing during `~EndpointManager` to @@ -625,7 +703,8 @@ void EndpointManager::DiscardEndpoint(ClientProxy* client, } RemoveEndpoint(client, endpoint_id, - /*notify=*/client->IsConnectedToEndpoint(endpoint_id)); + /* notify */client->IsConnectedToEndpoint(endpoint_id), + reason); }); } @@ -647,8 +726,12 @@ std::vector EndpointManager::SendControlMessage( // @EndpointManagerThread void EndpointManager::RemoveEndpoint(ClientProxy* client, const std::string& endpoint_id, - bool notify) { - NEARBY_LOGS(INFO) << "RemoveEndpoint for endpoint " << endpoint_id; + bool notify, DisconnectionReason reason) { + NEARBY_LOGS(INFO) << "RemoveEndpoint for endpoint: " << endpoint_id + << ", reason: " << reason; + + SafeDisconnectionResult safe_disconnect_result = + ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION; // Grab the service ID before we destroy the channel. EndpointChannel* channel = @@ -656,16 +739,35 @@ void EndpointManager::RemoveEndpoint(ClientProxy* client, std::string service_id = channel ? channel->GetServiceId() : std::string(kUnknownServiceId); + if (client->IsSafeToDisconnectEnabled(endpoint_id)) { + if (channel != nullptr) { + bool is_safe_disconnection = + ApplySafeToDisconnect(endpoint_id, channel, reason); + safe_disconnect_result = + is_safe_disconnection + ? ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION + : ConnectionsLog::EstablishedConnection::UNSAFE_DISCONNECTION; + NEARBY_LOGS(INFO) << "[safe-to-disconnect] safe_disconnect_result:" + << (safe_disconnect_result? "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 // this endpoint. - if (channel_manager_->UnregisterChannelForEndpoint(endpoint_id)) { + if (channel_manager_->UnregisterChannelForEndpoint(endpoint_id, reason, + safe_disconnect_result)) { // Notify all frame processors of the disconnection immediately and wait // for them to clean up state. Only once all processors are done cleaning // up, we can remove the endpoint from ClientProxy after which there // should be no further interactions with the endpoint. // (See b/37352254 for history) - WaitForEndpointDisconnectionProcessing(client, service_id, endpoint_id); + WaitForEndpointDisconnectionProcessing(client, service_id, endpoint_id, + reason); client->OnDisconnected(endpoint_id, notify); NEARBY_LOGS(INFO) << "Removed endpoint for endpoint " << endpoint_id; @@ -673,15 +775,68 @@ void EndpointManager::RemoveEndpoint(ClientProxy* client, RemoveEndpointState(endpoint_id); } +bool EndpointManager::ApplySafeToDisconnect(const std::string& endpoint_id, + EndpointChannel* endpoint_channel, + DisconnectionReason reason) { + NEARBY_LOGS(INFO) << "[safe-to-disconnect] ApplySafeToDisconnect reason: " + << reason; + bool is_safe_disconnection = false; + bool send_disconnection_frame = true; + switch (reason) { + case DisconnectionReason::UPGRADED: + case DisconnectionReason::SHUTDOWN: + case DisconnectionReason::UNFINISHED: + return true; // safe disconnection + case DisconnectionReason::IO_ERROR: + return false; // unsafe disconnection + case DisconnectionReason::LOCAL_DISCONNECTION: + is_safe_disconnection = true; + send_disconnection_frame = true; + break; + case DisconnectionReason::REMOTE_DISCONNECTION: + is_safe_disconnection = true; + send_disconnection_frame = false; + break; + default: + is_safe_disconnection = false; + send_disconnection_frame = true; + } + + if (send_disconnection_frame) { + // If the channel was paused (i.e. during a bandwidth upgrade negotiation) + // we resume to ensure the thread won't hang when trying to write to it. + endpoint_channel->Resume(); + NEARBY_LOGS(INFO) << "[safe-to-disconnect] Sending " + "DISCONNECTION frame with request 1, ack 0"; + Exception write_exception = endpoint_channel->Write( + parser::ForDisconnection(/* request_safe_to_disconnect= */ true, + /* ack_safe_to_disconnect= */ false)); + + if (!write_exception.Ok()) { + NEARBY_LOGS(WARNING) << "[safe-to-disconnect] Failed to send " + "DISCONNECTION frame to endpoint" + << endpoint_id << " for reason: " << reason; + return is_safe_disconnection; + } + } + + bool state = + channel_manager_->CreateNewTimeoutDisconnectedState(endpoint_id); + if (!state) return is_safe_disconnection; + + return is_safe_disconnection || + channel_manager_->IsSafeToDisconnect(endpoint_id); +} + // @EndpointManagerThread void EndpointManager::WaitForEndpointDisconnectionProcessing( ClientProxy* client, const std::string& service_id, - const std::string& endpoint_id) { + const std::string& endpoint_id, DisconnectionReason reason) { NEARBY_LOGS(INFO) << "Wait: client=" << client << "; service_id=" << service_id << "; endpoint_id=" << endpoint_id; CountDownLatch barrier = NotifyFrameProcessorsOnEndpointDisconnect( - client, service_id, endpoint_id); + client, service_id, endpoint_id, reason); NEARBY_LOGS(INFO) << "Waiting for frame processors to disconnect from endpoint " @@ -690,15 +845,15 @@ void EndpointManager::WaitForEndpointDisconnectionProcessing( NEARBY_LOGS(INFO) << "Failed to disconnect frame processors from endpoint " << endpoint_id; } else { - NEARBY_LOGS(INFO) - << "Finished waiting for frame processors to disconnect from endpoint " - << endpoint_id; + NEARBY_LOGS(INFO) << "Finished waiting for frame processors to " + "disconnect from endpoint " + << endpoint_id; } } CountDownLatch EndpointManager::NotifyFrameProcessorsOnEndpointDisconnect( ClientProxy* client, const std::string& service_id, - const std::string& endpoint_id) { + const std::string& endpoint_id, DisconnectionReason reason) { NEARBY_LOGS(INFO) << "NotifyFrameProcessorsOnEndpointDisconnect: client=" << client << "; service_id=" << service_id << "; endpoint_id=" << endpoint_id; @@ -714,7 +869,8 @@ CountDownLatch EndpointManager::NotifyFrameProcessorsOnEndpointDisconnect( << "; frame type=" << V1Frame::FrameType_Name(item.first); if (processor) { valid++; - processor->OnEndpointDisconnect(client, service_id, endpoint_id, barrier); + processor->OnEndpointDisconnect(client, service_id, endpoint_id, barrier, + reason); } else { barrier.CountDown(); } @@ -739,7 +895,8 @@ std::vector EndpointManager::SendTransferFrameBytes( if (channel == nullptr) { // We no longer know about this endpoint (it was either explicitly - // unregistered, or a read/write error made us unregister it internally). + // unregistered, or a read/write error made us unregister it + // internally). NEARBY_LOGS(ERROR) << "EndpointManager failed to find EndpointChannel " "over which to write " << packet_type << " at offset " << offset @@ -766,12 +923,15 @@ std::vector EndpointManager::SendTransferFrameBytes( EndpointManager::EndpointState::~EndpointState() { // We must unregister the endpoint first to signal the runnables that they - // should exit their loops. SingleThreadExecutor destructors will wait for the - // workers to finish. |channel_manager_| is null after moved from this object - // (in move constructor) which prevents unregistering the channel prematurely. + // should exit their loops. SingleThreadExecutor destructors will wait for + // the workers to finish. |channel_manager_| is null after moved from this + // object (in move constructor) which prevents unregistering the channel + // prematurely. if (channel_manager_) { NEARBY_LOG(VERBOSE, "EndpointState destructor %s", endpoint_id_.c_str()); - channel_manager_->UnregisterChannelForEndpoint(endpoint_id_); + channel_manager_->UnregisterChannelForEndpoint( + endpoint_id_, DisconnectionReason::SHUTDOWN, + ConnectionsLog::EstablishedConnection::SAFE_DISCONNECTION); } // Make sure the KeepAlive thread isn't blocking shutdown. diff --git a/connections/implementation/endpoint_manager.h b/connections/implementation/endpoint_manager.h index 5bfd0fbf..02306ca2 100644 --- a/connections/implementation/endpoint_manager.h +++ b/connections/implementation/endpoint_manager.h @@ -90,7 +90,8 @@ class EndpointManager { virtual void OnEndpointDisconnect(ClientProxy* client, const std::string& service_id, const std::string& endpoint_id, - CountDownLatch barrier) = 0; + CountDownLatch barrier, + DisconnectionReason reason) = 0; }; explicit EndpointManager(EndpointChannelManager* manager); @@ -154,7 +155,8 @@ class EndpointManager { // ask everyone who's registered an FrameProcessor to // processEndpointDisconnection() while the caller of DiscardEndpoint() is // blocked here. - void DiscardEndpoint(ClientProxy* client, const std::string& endpoint_id); + void DiscardEndpoint(ClientProxy* client, const std::string& endpoint_id, + DisconnectionReason reason); protected: // For unit tests only to control executing tasks on the executor. @@ -260,15 +262,21 @@ class EndpointManager { // this method is idempotent. // @EndpointManagerThread void RemoveEndpoint(ClientProxy* client, const std::string& endpoint_id, - bool notify); - + bool notify, DisconnectionReason reason); + bool ApplySafeToDisconnect(const std::string& endpoint_id, + EndpointChannel* endpoint_channel, + DisconnectionReason reason); void WaitForEndpointDisconnectionProcessing(ClientProxy* client, const std::string& service_id, - const std::string& endpoint_id); - + const std::string& endpoint_id, + DisconnectionReason reason); + void ProcessDisconnectionFrame( + ClientProxy* client, const std::string& endpoint_id, + EndpointChannel* endpoint_channel, + location::nearby::connections::OfflineFrame& frame); CountDownLatch NotifyFrameProcessorsOnEndpointDisconnect( ClientProxy* client, const std::string& service_id, - const std::string& endpoint_id); + const std::string& endpoint_id, DisconnectionReason reason); std::vector SendTransferFrameBytes( const std::vector& endpoint_ids, diff --git a/connections/implementation/endpoint_manager_test.cc b/connections/implementation/endpoint_manager_test.cc index 49d87f6a..d2047130 100644 --- a/connections/implementation/endpoint_manager_test.cc +++ b/connections/implementation/endpoint_manager_test.cc @@ -29,10 +29,13 @@ #include "connections/connection_options.h" #include "connections/implementation/client_proxy.h" #include "connections/implementation/endpoint_channel_manager.h" +#include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/offline_frames.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/feature_flags.h" #include "internal/platform/logging.h" #include "internal/test/fake_single_thread_executor.h" #include "proto/connections_enums.pb.h" @@ -112,10 +115,21 @@ class MockFrameProcessor : public EndpointManager::FrameProcessor { MOCK_METHOD(void, OnEndpointDisconnect, (ClientProxy * client, const std::string& service_id, - const std::string& endpoint_id, CountDownLatch barrier), + const std::string& endpoint_id, CountDownLatch barrier, + DisconnectionReason reason), (override)); }; +class SetSafeToDisconnect { + public: + explicit SetSafeToDisconnect(bool safe_to_disconnect) { + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature:: + kEnableSafeToDisconnect, + safe_to_disconnect); + } +}; + class TestEndpointManager : public EndpointManager { public: TestEndpointManager(EndpointChannelManager* manager, @@ -146,7 +160,7 @@ class EndpointManagerTest : public ::testing::Test { EXPECT_TRUE(done.Await(absl::Milliseconds(1000)).result()); } } - + SetSafeToDisconnect set_safe_to_disconnect_{true}; std::unique_ptr client_ = std::make_unique(); ConnectionOptions connection_options_{ .keep_alive_interval_millis = 5000, @@ -199,9 +213,9 @@ TEST_F(EndpointManagerTest, RegisterEndpointCallsOnConnectionInitiated) { } TEST_F(EndpointManagerTest, UnregisterEndpointCallsOnDisconnected) { - auto endpoint_channel = std::make_unique(); - EXPECT_CALL(*endpoint_channel, Read()) - .WillRepeatedly(Return(ExceptionOr(Exception::kIo))); +// auto endpoint_channel = std::make_unique(); +// EXPECT_CALL(*endpoint_channel, Read()) +// .WillRepeatedly(Return(ExceptionOr(Exception::kIo))); RegisterEndpoint(std::make_unique()); // NOTE: disconnect_cb is not called, because we did not reach fully connected // state. On top of that, UnregisterEndpoint is suppressing this notification. @@ -211,6 +225,19 @@ TEST_F(EndpointManagerTest, UnregisterEndpointCallsOnDisconnected) { em_.UnregisterEndpoint(client_.get(), endpoint_id_); } +TEST_F(EndpointManagerTest, + UnregisterEndpointCallsOnDisconnectedSafeToDisconnect) { + RegisterEndpoint(std::make_unique()); + // NOTE: disconnect_cb is not called, because we did not reach fully connected + // state. On top of that, UnregisterEndpoint is suppressing this notification. + // (IMO, it should be called as long as any connection callback was called + // before. (in this case initiated_cb is called)). + // Test captures current protocol behavior. + client_->SetRemoteSafeToDisconnectVersion(endpoint_id_, 2); + ecm_.UpdateSafeToDisconnectForEndpoint(endpoint_id_, true); + em_.UnregisterEndpoint(client_.get(), endpoint_id_); +} + TEST_F(EndpointManagerTest, RegisterFrameProcessorWorks) { auto endpoint_channel = std::make_unique(); auto connect_request = std::make_unique(); @@ -434,7 +461,8 @@ TEST_F(EndpointManagerTest, DisconnectEndpointDuringDestruction) { // immediately. fake_serial_executor->SetRunExecutablesImmediately( /*run_executables_immediately=*/false); - endpoint_manager->DiscardEndpoint(client_.get(), endpoint_id_); + endpoint_manager->DiscardEndpoint(client_.get(), endpoint_id_, + DisconnectionReason::IO_ERROR); // Simulate Core destruction of ClientProxy by destroying `client_`. client_.reset(); diff --git a/connections/implementation/flags/nearby_connections_feature_flags.h b/connections/implementation/flags/nearby_connections_feature_flags.h index 430be69a..d7f6a398 100644 --- a/connections/implementation/flags/nearby_connections_feature_flags.h +++ b/connections/implementation/flags/nearby_connections_feature_flags.h @@ -15,6 +15,7 @@ #ifndef THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_FLAGS_NEARBY_CONNECTIONS_FEATURE_FLAGS_H_ #define THIRD_PARTY_NEARBY_CONNECTIONS_IMPLEMENTATION_FLAGS_NEARBY_CONNECTIONS_FEATURE_FLAGS_H_ +#include #include "absl/strings/string_view.h" #include "internal/flags/flag.h" @@ -27,7 +28,6 @@ constexpr absl::string_view kConfigPackage = "nearby"; // The Nearby Connections features. namespace nearby_connections_feature { -// LINT.IfChanged // Disable/Enable BLE v2 in Nearby Connections SDK. constexpr auto kEnableBleV2 = flags::Flag(kConfigPackage, "45401515", false); @@ -44,11 +44,18 @@ constexpr auto kEnableGattQueryInThread = constexpr auto kEnablePayloadManagerToSkipChunkUpdate = flags::Flag(kConfigPackage, "45415729", false); -// LINT.ThenChange( -// //depot/google3/location/nearby/cpp/sharing/clients/cpp/nearby_sharing_service_adapter_dart.h, -// //depot/google3/location/nearby/cpp/sharing/clients/cpp/nearby_sharing_service_adapter_dart.cc, -// //depot/google3/location/nearby/cpp/sharing/clients/dart/platform/lib/types/models.dart -// ) +// Enable/Disable safe-to-disconnect feature. +constexpr auto kEnableSafeToDisconnect = + flags::Flag(kConfigPackage, "45425789", false); + +// When true, allows to enable payload-received-ack protocol. +constexpr auto kEnablePayloadReceivedAck = + flags::Flag(kConfigPackage, "45425840", false); + +// Support 1. safe-to-disconnect 2. reserved 3. auto-reconnect +// 4. auto-resume for dev device 5. payload_ack +constexpr auto kSafeToDisconnectVersion = + flags::Flag(kConfigPackage, "45425841", 2); } // namespace nearby_connections_feature } // namespace config_package_nearby diff --git a/connections/implementation/fuzzers/BUILD b/connections/implementation/fuzzers/BUILD index 64223d93..54b9df9b 100644 --- a/connections/implementation/fuzzers/BUILD +++ b/connections/implementation/fuzzers/BUILD @@ -23,6 +23,7 @@ cc_fuzz_target( deps = [ "//connections/implementation:internal", "//internal/platform:base", + "//internal/platform/implementation/g3", "//security/fuzzing/blaze:default_init_google_for_cc_fuzz_target", ], ) diff --git a/connections/implementation/offline_frames.cc b/connections/implementation/offline_frames.cc index fcebbddc..395d2a99 100644 --- a/connections/implementation/offline_frames.cc +++ b/connections/implementation/offline_frames.cc @@ -14,14 +14,17 @@ #include "connections/implementation/offline_frames.h" +#include #include #include #include #include +#include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/offline_frames_validator.h" #include "connections/implementation/proto/offline_wire_formats.pb.h" #include "connections/status.h" +#include "internal/flags/nearby_flags.h" #include "internal/platform/byte_array.h" namespace nearby { @@ -165,7 +168,8 @@ ByteArray ForConnectionRequestPresence( return ToBytes(std::move(frame)); } -ByteArray ForConnectionResponse(std::int32_t status, const OsInfo& os_info) { +ByteArray ForConnectionResponse( + std::int32_t status, const OsInfo& os_info) { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -181,6 +185,10 @@ ByteArray ForConnectionResponse(std::int32_t status, const OsInfo& os_info) { ? ConnectionResponseFrame::ACCEPT : ConnectionResponseFrame::REJECT); *sub_frame->mutable_os_info() = os_info; + sub_frame->set_safe_to_disconnect_version( + NearbyFlags::GetInstance().GetInt64Flag( + config_package_nearby::nearby_connections_feature:: + kSafeToDisconnectVersion)); return ToBytes(std::move(frame)); } @@ -445,13 +453,16 @@ ByteArray ForKeepAlive() { return ToBytes(std::move(frame)); } -ByteArray ForDisconnection() { +ByteArray ForDisconnection(bool request_safe_to_disconnect, + bool ack_safe_to_disconnect) { OfflineFrame frame; frame.set_version(OfflineFrame::V1); auto* v1_frame = frame.mutable_v1(); v1_frame->set_type(V1Frame::DISCONNECTION); - v1_frame->mutable_disconnection(); + auto* disconnection = v1_frame->mutable_disconnection(); + 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)); } diff --git a/connections/implementation/offline_frames.h b/connections/implementation/offline_frames.h index 6a66d9f7..fb6214ab 100644 --- a/connections/implementation/offline_frames.h +++ b/connections/implementation/offline_frames.h @@ -98,8 +98,8 @@ ByteArray ForBwuLastWrite(); ByteArray ForBwuSafeToClose(); ByteArray ForKeepAlive(); -ByteArray ForDisconnection(); - +ByteArray ForDisconnection(bool request_safe_to_disconnect, + bool ack_safe_to_disconnect); 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 a1b14320..55d1e8a1 100644 --- a/connections/implementation/offline_frames_test.cc +++ b/connections/implementation/offline_frames_test.cc @@ -269,6 +269,7 @@ TEST(OfflineFramesTest, CanGenerateConnectionResponse) { status: 1 response: REJECT os_info { type: LINUX } + safe_to_disconnect_version: 2 > >)pb"; @@ -538,6 +539,25 @@ TEST(OfflineFramesTest, CanGenerateKeepAlive) { EXPECT_THAT(message, EqualsProto(kExpected)); } +TEST(OfflineFramesTest, CanGenerateDisconnection) { + constexpr absl::string_view kExpected = + R"pb( + version: V1 + v1: < + type: DISCONNECTION + disconnection: < + request_safe_to_disconnect: true + ack_safe_to_disconnect: true + > + >)pb"; + ByteArray bytes = ForDisconnection(/* request_safe_to_disconnect */ true, + /* ack_safe_to_disconnect */ true); + auto response = FromBytes(bytes); + ASSERT_TRUE(response.ok()); + OfflineFrame message = response.result(); + EXPECT_THAT(message, EqualsProto(kExpected)); +} + } // namespace } // namespace parser } // namespace connections diff --git a/connections/implementation/offline_service_controller_test.cc b/connections/implementation/offline_service_controller_test.cc index 5b4e0989..5173dc1f 100644 --- a/connections/implementation/offline_service_controller_test.cc +++ b/connections/implementation/offline_service_controller_test.cc @@ -94,6 +94,9 @@ class OfflineServiceControllerTest void SetUp() override { NearbyFlags::GetInstance().OverrideBoolFlagValue( config_package_nearby::nearby_connections_feature::kEnableBleV2, true); + NearbyFlags::GetInstance().OverrideBoolFlagValue( + config_package_nearby::nearby_connections_feature:: + kEnableSafeToDisconnect, false); } bool SetupConnection(OfflineSimulationUser& user_a, OfflineSimulationUser& user_b) { diff --git a/connections/implementation/payload_manager.cc b/connections/implementation/payload_manager.cc index 1eec8436..349b8cb2 100644 --- a/connections/implementation/payload_manager.cc +++ b/connections/implementation/payload_manager.cc @@ -15,7 +15,7 @@ #include "connections/implementation/payload_manager.h" #include -#include +#include #include #include #include @@ -24,24 +24,30 @@ #include "absl/functional/any_invocable.h" #include "absl/functional/bind_front.h" -#include "absl/memory/memory.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/time/time.h" #include "connections/implementation/analytics/throughput_recorder.h" +#include "connections/implementation/client_proxy.h" +#include "connections/implementation/endpoint_channel_manager.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "connections/implementation/internal_payload_factory.h" +#include "connections/payload_type.h" #include "internal/flags/nearby_flags.h" #include "internal/platform/count_down_latch.h" +#include "internal/platform/exception.h" +#include "internal/platform/feature_flags.h" #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 { using ::location::nearby::connections::OfflineFrame; using ::location::nearby::connections::V1Frame; +using ::location::nearby::proto::connections::PayloadStatus; using ::nearby::analytics::PacketMetaData; using ::nearby::analytics::ThroughputRecorderContainer; using ::nearby::connections::PayloadDirection; @@ -158,7 +164,7 @@ bool PayloadManager::SendPayloadLoop( location::nearby::proto::connections:: PayloadStatus::ENDPOINT_IO_ERROR); } - + bool is_last_chunk = IsLastChunk(payload_chunk); // Check whether at least one endpoint succeeded -- if they all failed, // we'll just go right back to the top of the loop and break out when // availableEndpointIds is re-synced and found to be empty at that point. @@ -166,6 +172,12 @@ bool PayloadManager::SendPayloadLoop( for (const auto& endpoint_id : available_endpoint_ids) { if (std::find(failed_endpoint_ids.begin(), failed_endpoint_ids.end(), endpoint_id) == failed_endpoint_ids.end()) { + if (!WaitForReceivedAck(client, endpoint_id, pending_payload, + payload_header, next_chunk_offset, + is_last_chunk)) { + continue; + } + HandleSuccessfulOutgoingChunk( client, endpoint_id, payload_header, payload_chunk.flags(), payload_chunk.offset(), payload_chunk.body().size()); @@ -524,7 +536,8 @@ void PayloadManager::OnIncomingFrame( void PayloadManager::OnEndpointDisconnect(ClientProxy* client, const std::string& service_id, const std::string& endpoint_id, - CountDownLatch barrier) { + CountDownLatch barrier, + DisconnectionReason reason) { if (shutdown_.Get()) { barrier.CountDown(); return; @@ -532,7 +545,7 @@ void PayloadManager::OnEndpointDisconnect(ClientProxy* client, RunOnStatusUpdateThread( "payload-manager-on-disconnect", [this, client, endpoint_id, - barrier]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() mutable { + barrier, reason]() RUN_ON_PAYLOAD_STATUS_UPDATE_THREAD() mutable { // Iterate through all our payloads and look for payloads associated // with this endpoint. MutexLock lock(&mutex_); @@ -561,14 +574,27 @@ void PayloadManager::OnEndpointDisconnect(ClientProxy* client, // Send a client notification of a payload transfer failure. client->OnPayloadProgress(endpoint_id, update); + PayloadStatus payload_status; + switch (reason) { + case DisconnectionReason::LOCAL_DISCONNECTION: + payload_status = PayloadStatus::LOCAL_CLIENT_DISCONNECTION; + break; + case DisconnectionReason::REMOTE_DISCONNECTION: + payload_status = PayloadStatus::REMOTE_CLIENT_DISCONNECTION; + break; + case DisconnectionReason::IO_ERROR: + default: + payload_status = PayloadStatus::ENDPOINT_IO_ERROR; + break; + } + + if (pending_payload->IsIncoming()) { client->GetAnalyticsRecorder().OnIncomingPayloadDone( - endpoint_id, pending_payload->GetId(), - location::nearby::proto::connections::ENDPOINT_IO_ERROR); + endpoint_id, pending_payload->GetId(), payload_status); } else { client->GetAnalyticsRecorder().OnOutgoingPayloadDone( - endpoint_id, pending_payload->GetId(), - location::nearby::proto::connections::ENDPOINT_IO_ERROR); + endpoint_id, pending_payload->GetId(), payload_status); } }); @@ -803,6 +829,99 @@ void PayloadManager::SendControlMessage( endpoint_ids); } +void PayloadManager::SendPayloadReceivedAck( + ClientProxy* client, PendingPayload& pending_payload, + const std::string& endpoint_id, + const PayloadTransferFrame::PayloadHeader& payload_header, + std::int64_t chunk_size, bool is_last_chunk) { + if (!is_last_chunk || + !IsPayloadReceivedAckEnabled(client, endpoint_id, pending_payload)) { + return; + } + // Send the PAYLOAD_RECEIVED_ACK to the remote endpoint for the sender asap. + NEARBY_LOGS(INFO) + << "[PAYLOAD_RECEIVED_ACK] isLastChunk, receiver send ack to " + << endpoint_id; + + SendControlMessage( + {endpoint_id}, payload_header, chunk_size, + PayloadTransferFrame::ControlMessage::PAYLOAD_RECEIVED_ACK); +} + +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) { + if (!is_last_chunk || + !IsPayloadReceivedAckEnabled(client, endpoint_id, pending_payload)) { + return true; + } + + NEARBY_LOGS(INFO) << "[safe-to-disconnect] Last Chunk, sender wait for " + "PAYLOAD_RECEIVED_ACK frame from: " + << endpoint_id; + while (true) { + PendingPayloadHandle latest_pending_payload = + GetPayload(payload_header.id()); + // Make sure we're still tracking this payload and its associated endpoint. + if (!latest_pending_payload) { + return false; + } + + auto* endpoint_info = latest_pending_payload->GetEndpoint(endpoint_id); + if (endpoint_info == nullptr) { + return false; + } + + // Local payload cancellation + if (latest_pending_payload->IsLocallyCanceled()) { + HandleFinishedOutgoingPayload(client, {endpoint_id}, payload_header, + payload_chunk_offset, + location::nearby::proto::connections:: + PayloadStatus::LOCAL_CANCELLATION); + return false; + } + // Remote payload cancellation, etc + if (!endpoint_info->IsEndpointAvailable(client, + endpoint_info->status.Get())) { + HandleFinishedOutgoingPayload( + client, {endpoint_id}, payload_header, payload_chunk_offset, + EndpointInfoStatusToPayloadStatus(endpoint_info->status.Get())); + return false; + } + { + MutexLock lock(&endpoint_info->payload_received_ack_mutex); + if (endpoint_info->is_payload_received_ack) { + endpoint_info->is_payload_received_ack = false; + return true; + } + Exception wait_exception = endpoint_info->payload_received_ack_cond.Wait( + FeatureFlags::GetInstance() + .GetFlags() + .wait_payload_received_ack_millis); + endpoint_info->is_payload_received_ack = false; + if (!wait_exception.Ok()) { + return false; + } + return true; + } + } + return true; +} + +bool PayloadManager::IsPayloadReceivedAckEnabled( + ClientProxy* client, const std::string& endpoint_id, + PendingPayload& pending_payload) { + return NearbyFlags::GetInstance().GetBoolFlag( + config_package_nearby::nearby_connections_feature:: + kEnablePayloadReceivedAck) && + client->IsPayloadReceivedAckEnabled(endpoint_id) && + (pending_payload.GetInternalPayload()->GetType() != + nearby::connections::PayloadTransferFrame::PayloadTransferFrame:: + PayloadHeader::BYTES); +} + void PayloadManager::HandleFinishedOutgoingPayload( ClientProxy* client, const EndpointIds& finished_endpoint_ids, const PayloadTransferFrame::PayloadHeader& payload_header, @@ -833,7 +952,8 @@ void PayloadManager::HandleFinishedOutgoingPayload( // Unregister these endpoints, since we had an IO error on the physical // connection. for (const auto& endpoint_id : finished_endpoint_ids) { - endpoint_manager_->DiscardEndpoint(client, endpoint_id); + endpoint_manager_->DiscardEndpoint(client, endpoint_id, + DisconnectionReason::IO_ERROR); } break; case location::nearby::proto::connections::PayloadStatus::REMOTE_ERROR: @@ -1124,6 +1244,7 @@ void PayloadManager::ProcessDataPacket( // Save size of packet before we move it. std::int64_t payload_body_size = payload_chunk.body().size(); + packet_meta_data.StartFileIo(); if (pending_payload->GetInternalPayload() ->AttachNextChunk(ByteArray(std::move(*payload_chunk.mutable_body()))) @@ -1137,6 +1258,11 @@ void PayloadManager::ProcessDataPacket( 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, payload_header, + payload_chunk.offset() + payload_body_size, is_last_chunk); HandleSuccessfulIncomingChunk(to_client, from_endpoint_id, payload_header, payload_chunk.flags(), payload_chunk.offset(), @@ -1145,8 +1271,6 @@ void PayloadManager::ProcessDataPacket( ThroughputRecorderContainer::GetInstance() .GetTPRecorder(payload_header.id(), PayloadDirection::INCOMING_PAYLOAD) ->OnFrameReceived(medium, packet_meta_data); - bool is_last_chunk = (payload_chunk.flags() & - PayloadTransferFrame::PayloadChunk::LAST_CHUNK) != 0; if (is_last_chunk) { ThroughputRecorderContainer::GetInstance() .GetTPRecorder(payload_header.id(), PayloadDirection::INCOMING_PAYLOAD) @@ -1206,6 +1330,17 @@ void PayloadManager::ProcessControlPacket( control_message); } break; + case PayloadTransferFrame::ControlMessage::PAYLOAD_RECEIVED_ACK: + if (!pending_payload->IsIncoming() && + IsPayloadReceivedAckEnabled(to_client, from_endpoint_id, + *pending_payload)) { + NEARBY_LOGS(INFO) << "[safe-to-disconnect]Sender received " + "PAYLOAD_RECEIVED_ACK frame with id:" + << pending_payload->GetInternalPayload()->GetId() + << " from endpoint_id=" << from_endpoint_id; + pending_payload->MarkReceivedAckFromEndpoint(from_endpoint_id); + } + break; default: NEARBY_LOGS(INFO) << "Unhandled control message " << control_message.event() << " for payload_id=" @@ -1290,8 +1425,26 @@ void PayloadManager::EndpointInfo::SetStatusFromControlMessage( << " based on OOB ControlMessage"; } -//////////////////////////////// PendingPayload -/////////////////////////////////// +void PayloadManager::EndpointInfo::MarkReceivedAckFromEndpoint() { + MutexLock lock(&payload_received_ack_mutex); + is_payload_received_ack = true; + payload_received_ack_cond.Notify(); +} + +bool PayloadManager::EndpointInfo::IsEndpointAvailable( + ClientProxy* clientProxy, EndpointInfo::Status status) { + // Pending endpointIds would be removed from the payload after + // onPayloadTransferUpdate, but there is the racing problem that gets the + // available endpoints before update. Here force to remove those endpoints + // (b/227419433). + bool is_pending_endpoint = false; + if (clientProxy->HasPendingConnectionToEndpoint(id)) { + is_pending_endpoint = true; + } + return (status == EndpointInfo::Status::kAvailable) && !is_pending_endpoint; +} + +//////////////////////////////// PendingPayload //////////////////////////////// PayloadManager::PendingPayload::PendingPayload( std::unique_ptr internal_payload, @@ -1305,7 +1458,7 @@ PayloadManager::PendingPayload::PendingPayload( // failures. Any of these situations will cause endpoint to be marked as // unavailable. for (const auto& id : endpoint_ids) { - EndpointInfo endpoint_info{}; + EndpointInfo endpoint_info; endpoint_info.id = id; endpoint_info.status.Set(EndpointInfo::Status::kAvailable); @@ -1329,6 +1482,13 @@ void PayloadManager::PendingPayload::MarkLocallyCanceled() { is_locally_canceled_.Set(true); } +void PayloadManager::PendingPayload::MarkReceivedAckFromEndpoint( + const std::string& from_endpoint_id) { + auto info = GetEndpoint(from_endpoint_id); + if (!info) return; + info->MarkReceivedAckFromEndpoint(); +} + bool PayloadManager::PendingPayload::IsIncoming() const { return is_incoming_; } std::vector diff --git a/connections/implementation/payload_manager.h b/connections/implementation/payload_manager.h index eeb46b93..768841d1 100644 --- a/connections/implementation/payload_manager.h +++ b/connections/implementation/payload_manager.h @@ -35,6 +35,7 @@ #include "internal/platform/atomic_boolean.h" #include "internal/platform/atomic_reference.h" #include "internal/platform/byte_array.h" +#include "internal/platform/condition_variable.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/mutex.h" @@ -70,7 +71,8 @@ class PayloadManager : public EndpointManager::FrameProcessor { // @EndpointManagerThread void OnEndpointDisconnect(ClientProxy* client, const std::string& service_id, const std::string& endpoint_id, - CountDownLatch barrier) override; + CountDownLatch barrier, + DisconnectionReason reason) override; void DisconnectFromEndpointManager(); @@ -92,10 +94,17 @@ class PayloadManager : public EndpointManager::FrameProcessor { static Status ControlMessageEventToEndpointInfoStatus( PayloadTransferFrame::ControlMessage::EventType event); + void MarkReceivedAckFromEndpoint(); + bool IsEndpointAvailable(ClientProxy* clientProxy, + EndpointInfo::Status status); std::string id; AtomicReference status{Status::kUnknown}; std::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) = + false; }; // Tracks state for an InternalPayload and the endpoints associated with it. @@ -121,6 +130,7 @@ class PayloadManager : public EndpointManager::FrameProcessor { bool IsLocallyCanceled() const; void MarkLocallyCanceled(); + void MarkReceivedAckFromEndpoint(const std::string& from_endpoint_id); bool IsIncoming() const; // Gets the EndpointInfo objects for the endpoints (still) associated with @@ -289,6 +299,10 @@ class PayloadManager : public EndpointManager::FrameProcessor { PayloadTransferFrame::PayloadChunk CreatePayloadChunk(std::int64_t offset, ByteArray body); + bool IsLastChunk(PayloadTransferFrame::PayloadChunk payload_chunk) { + return ((payload_chunk.flags() & + PayloadTransferFrame::PayloadChunk::LAST_CHUNK) != 0); + } PendingPayloadHandle CreateIncomingPayload(const PayloadTransferFrame& frame, const std::string& endpoint_id) @@ -315,6 +329,21 @@ class PayloadManager : public EndpointManager::FrameProcessor { std::int64_t num_bytes_successfully_transferred, PayloadTransferFrame::ControlMessage::EventType event_type); + void SendPayloadReceivedAck( + ClientProxy* client, PendingPayload& pending_payload, + const std::string& endpoint_id, + const PayloadTransferFrame::PayloadHeader& payload_header, + std::int64_t chunk_size, bool is_last_chunk); + + bool 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); + bool IsPayloadReceivedAckEnabled(ClientProxy* client, + const std::string& endpoint_id, + PendingPayload& pending_payload); + // Handles a finished outgoing payload for the given endpointIds. All // statuses except for SUCCESS are handled here. void HandleFinishedOutgoingPayload( diff --git a/connections/implementation/simulation_user.h b/connections/implementation/simulation_user.h index ef68db3d..7a66e4b7 100644 --- a/connections/implementation/simulation_user.h +++ b/connections/implementation/simulation_user.h @@ -15,6 +15,7 @@ #ifndef CORE_INTERNAL_SIMULATION_USER_H_ #define CORE_INTERNAL_SIMULATION_USER_H_ +#include #include #include "gtest/gtest.h" @@ -22,13 +23,15 @@ #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/injected_bluetooth_device_store.h" #include "connections/implementation/payload_manager.h" #include "connections/implementation/pcp_manager.h" +#include "internal/flags/nearby_flags.h" #include "internal/platform/condition_variable.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" // Test-only class to help run end-to-end simulations for nearby connections // protocol. @@ -39,6 +42,26 @@ namespace nearby { namespace connections { +class SetSafeToDisconnect { + public: + 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:: + kEnablePayloadReceivedAck, + payload_received_ack); + NearbyFlags::GetInstance().OverrideInt64FlagValue( + config_package_nearby::nearby_connections_feature:: + kSafeToDisconnectVersion, + safe_to_disconnect_version); + } +}; + class SimulationUser { public: struct DiscoveredInfo { @@ -176,6 +199,7 @@ class SimulationUser { AdvertisingOptions advertising_options_; ConnectionOptions connection_options_; DiscoveryOptions discovery_options_; + SetSafeToDisconnect set_safe_to_disconnect_{true, true, 2}; ClientProxy client_; EndpointChannelManager ecm_; EndpointManager em_{&ecm_}; diff --git a/internal/platform/feature_flags.h b/internal/platform/feature_flags.h index 58f8835d..d4202188 100644 --- a/internal/platform/feature_flags.h +++ b/internal/platform/feature_flags.h @@ -15,6 +15,8 @@ #ifndef PLATFORM_BASE_FEATURE_FLAGS_H_ #define PLATFORM_BASE_FEATURE_FLAGS_H_ +#include + #include "absl/synchronization/mutex.h" #include "absl/time/time.h" @@ -60,6 +62,18 @@ class FeatureFlags { // requested service id before attempting to connect over rfcomm. SDP fails // on Windows when connecting to FP service id but the rfcomm is successful. bool skip_service_discovery_before_connecting_to_rfcomm = false; + std::int32_t min_nc_version_supports_safe_to_disconnect = 1; + // 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. + std::int32_t min_nc_version_supports_payload_received_ack = 5; + // If the other part doesn't ack the safe_to_disconnect request, the + // initiator will end the connection in 30s. + absl::Duration safe_to_disconnect_ack_delay_millis = + absl::Milliseconds(30000); + // If the receiver doesn't ack with payload_received_ack frame in 1s, the + // sender will timeout the waiting. + absl::Duration wait_payload_received_ack_millis = absl::Milliseconds(1000); }; static const FeatureFlags& GetInstance() { From 0d83625766a0be92e713d592a3c8bcc7fd6d3307 Mon Sep 17 00:00:00 2001 From: Xin He Date: Fri, 25 Aug 2023 13:30:43 -0700 Subject: [PATCH 128/128] [Presence] Add DUSI to shared credential PiperOrigin-RevId: 560181461 --- internal/proto/credential.proto | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/proto/credential.proto b/internal/proto/credential.proto index c334d24c..42836b1d 100644 --- a/internal/proto/credential.proto +++ b/internal/proto/credential.proto @@ -43,7 +43,7 @@ enum CredentialType { // The shared credential is derived from local credential, and distributed to // remote devices based on the trust token for identity decryption and // authentication. -// NEXT_ID=15 +// NEXT_ID=16 // LINT.IfChange(SharedCredential) message SharedCredential { // The randomly generated unique id of the public credential. @@ -97,5 +97,9 @@ message SharedCredential { // The randomly generated positive unique id of the shared credential. int64 id = 14; + + // The DUSI number related to the uploader of this shared credential. Debug + // purpose only. + string dusi = 15; } // LINT.ThenChange(//depot/google3/google/internal/location/nearby/presence/v1/nearby_resources.proto:SharedCredential)