diff --git a/connections/c/nc.cc b/connections/c/nc.cc index d90f9fac..128eeb29 100644 --- a/connections/c/nc.cc +++ b/connections/c/nc.cc @@ -840,12 +840,3 @@ void NcSetPhenotypeFlagReader(READER_CONTEXT context, context, phenotype_flag_reader); nearby::NearbyFlags::GetInstance().SetFlagReader(*kNearbyFlags); } - -void NcSaveStates(NC_INSTANCE instance) { - NcContext* nc_context = GetContext(instance); - if (nc_context == nullptr) { - return; - } - - nc_context->core->SaveStates(); -} diff --git a/connections/c/nc.h b/connections/c/nc.h index 24e70ef9..1265fcba 100644 --- a/connections/c/nc.h +++ b/connections/c/nc.h @@ -196,10 +196,6 @@ NC_API void NcSetCustomSavePath(NC_INSTANCE instance, const NC_DATA* save_path, NC_API void NcSetPhenotypeFlagReader( READER_CONTEXT context, NC_PHENOTYPE_FLAG_READER phenotype_flag_reader); -// Saves the states of Nearby Connections to preferences. This is used to -// restore the states when restart the Nearby Connections. -NC_API void NcSaveStates(NC_INSTANCE instance); - #ifdef __cplusplus } // extern "C" #endif // __cplusplus diff --git a/connections/core.cc b/connections/core.cc index ffb3a468..61b77422 100644 --- a/connections/core.cc +++ b/connections/core.cc @@ -207,8 +207,6 @@ void Core::SetCustomSavePath(absl::string_view path, ResultCallback callback) { router_->SetCustomSavePath(&client_, path, std::move(callback)); } -void Core::SaveStates() { client_.SaveClientInfoToPreferences(); } - std::string Core::Dump() { return client_.Dump(); } // V3 diff --git a/connections/core.h b/connections/core.h index ea94b3b4..d159767a 100644 --- a/connections/core.h +++ b/connections/core.h @@ -258,10 +258,6 @@ class Core { // Gets the local endpoint generated by Nearby Connections. std::string GetLocalEndpointId() { return client_.GetLocalEndpointId(); } - // Saves the states of Nearby Connections to preferences. This is used to - // restore the states when restart the Nearby Connections. - void SaveStates(); - std::string Dump(); //******************************* V3 ******************************* diff --git a/connections/implementation/client_proxy.cc b/connections/implementation/client_proxy.cc index e9bde163..fcb28b9c 100644 --- a/connections/implementation/client_proxy.cc +++ b/connections/implementation/client_proxy.cc @@ -64,6 +64,7 @@ #include "internal/platform/error_code_params.h" #include "internal/platform/error_code_recorder.h" #include "internal/platform/feature_flags.h" +#include "internal/platform/implementation/app_lifecycle_monitor.h" #include "internal/platform/implementation/platform.h" #include "internal/platform/implementation/system_clock.h" #include "internal/platform/logging.h" @@ -138,6 +139,19 @@ ClientProxy::ClientProxy(::nearby::analytics::EventLogger* event_logger) // Load advertising info from preferences. LoadClientInfoFromPreferences(); + + if (preferences_manager_ != nullptr) { + app_lifecycle_monitor_ = + api::ImplementationPlatform::CreateAppLifecycleMonitor( + [this](api::AppLifecycleMonitor::AppLifecycleState state) { + if (state == + api::AppLifecycleMonitor::AppLifecycleState::kInactive || + state == api::AppLifecycleMonitor::AppLifecycleState:: + kBackground) { + SaveClientInfoToPreferences(); + } + }); + } } ClientProxy::~ClientProxy() { Reset(); } diff --git a/connections/implementation/client_proxy.h b/connections/implementation/client_proxy.h index 3817afe2..e10a74f9 100644 --- a/connections/implementation/client_proxy.h +++ b/connections/implementation/client_proxy.h @@ -45,6 +45,7 @@ #include "internal/platform/cancelable_alarm.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/error_code_recorder.h" +#include "internal/platform/implementation/app_lifecycle_monitor.h" #include "internal/platform/implementation/preferences_manager.h" #include "internal/platform/mac_address.h" #include "internal/platform/mutex.h" @@ -541,6 +542,9 @@ class ClientProxy final { std::unique_ptr default_cancellation_flag_ = std::make_unique(true); + // An app lifecycle monitor for monitoring the app lifecycle state. + std::unique_ptr app_lifecycle_monitor_; + // A preferences manager for storing client-specific preferences. std::unique_ptr preferences_manager_; diff --git a/internal/platform/implementation/BUILD b/internal/platform/implementation/BUILD index a3e1cadf..4af3c20c 100644 --- a/internal/platform/implementation/BUILD +++ b/internal/platform/implementation/BUILD @@ -149,6 +149,7 @@ cc_library( cc_library( name = "comm", hdrs = [ + "app_lifecycle_monitor.h", "awdl.h", "ble.h", "bluetooth_adapter.h", diff --git a/internal/platform/implementation/app_lifecycle_monitor.h b/internal/platform/implementation/app_lifecycle_monitor.h new file mode 100644 index 00000000..39f53370 --- /dev/null +++ b/internal/platform/implementation/app_lifecycle_monitor.h @@ -0,0 +1,57 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APP_LIFECYCLE_MONITOR_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APP_LIFECYCLE_MONITOR_H_ + +#include +#include + +namespace nearby { +namespace api { + +class AppLifecycleMonitor { + public: + // The state of the app lifecycle. The state transition is kBackground -> + // kInactive -> kActive. Foreground is an umbrella term that describes the app + // when it is either Inactive or Active. + enum class AppLifecycleState { + // The app is active when it starts to receive events. + kActive, + // The app is inactive but is still not in background. + kInactive, + // The app is background. + kBackground, + }; + + // Registers callbacks for connection changes. The callbacks will be called + // when device is connected to a LAN network or when the device is connected + // to internet. + explicit AppLifecycleMonitor( + std::function state_updated_callback) + : state_updated_callback_(std::move(state_updated_callback)) {} + + virtual ~AppLifecycleMonitor() = default; + + protected: + // The callback to be called when the app lifecycle state is updated. In + // order to make it possible to be called from different methods, use + // std::function instead of absl::AnyInvocable. + std::function state_updated_callback_; +}; + +} // namespace api +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APP_LIFECYCLE_MONITOR_H_ diff --git a/internal/platform/implementation/apple/BUILD b/internal/platform/implementation/apple/BUILD index 6050c2ff..da9b448b 100644 --- a/internal/platform/implementation/apple/BUILD +++ b/internal/platform/implementation/apple/BUILD @@ -82,6 +82,7 @@ objc_library( ":Platform_cc", ":Shared", ":ble_v2", + ":app_lifecycle_monitor", ":network_utils", # Required Reason API File: third_party/nearby/internal/platform/implementation/apple/preferences_manager.mm "//releasetools/apple/privacy/privacymanifests/requiredreasonsapi:user_defaults-user_defaults-read_write_app_data_ca92_1", @@ -115,6 +116,38 @@ objc_library( }), ) +objc_library( + name = "GNCNotificationCenter", + hdrs = ["GNCNotificationCenter.h"], + deps = [ + "//third_party/apple_frameworks:Foundation", + ], +) + +objc_library( + name = "app_lifecycle_monitor", + srcs = [ + "app_lifecycle_monitor.mm", + ], + hdrs = [ + "app_lifecycle_monitor.h", + ], + # Prevent Objective-C++ headers from being pulled into swift. + aspect_hints = ["//tools/build_defs/swift:no_module"], + deps = [ + ":GNCNotificationCenter", + "//internal/platform:base", + "//internal/platform/implementation:comm", + "//internal/platform/implementation/apple/Log:GNCLogger", + "//third_party/apple_frameworks:Foundation", + ] + select({ + "@platforms//os:platform_ios": [ + "//third_party/apple_frameworks:UIKit", + ], + "//conditions:default": [], + }), +) + objc_library( name = "ble_v2", srcs = [ diff --git a/internal/platform/implementation/apple/GNCNotificationCenter.h b/internal/platform/implementation/apple/GNCNotificationCenter.h new file mode 100644 index 00000000..287f0019 --- /dev/null +++ b/internal/platform/implementation/apple/GNCNotificationCenter.h @@ -0,0 +1,40 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_GNCNOTIFICATIONCENTER_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_GNCNOTIFICATIONCENTER_H_ + +#import + +@protocol GNCNotificationCenter + +// Method for registering an observer +- (id)addObserverForName:(NSNotificationName)name + object:(id)obj + queue:(NSOperationQueue *)queue + usingBlock:(void (^)(NSNotification *notification))block; + +// Method for unregistering an observer +- (void)removeObserver:(id)observer; + +// Method for posting a notification +- (void)postNotificationName:(NSNotificationName)aName + object:(nullable id)anObject; + +@end + +@interface NSNotificationCenter () +@end + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_GNCNOTIFICATIONCENTER_H_ diff --git a/internal/platform/implementation/apple/app_lifecycle_monitor.h b/internal/platform/implementation/apple/app_lifecycle_monitor.h new file mode 100644 index 00000000..fd5b58ac --- /dev/null +++ b/internal/platform/implementation/apple/app_lifecycle_monitor.h @@ -0,0 +1,46 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_APP_LIFECYCLE_MONITOR_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_APP_LIFECYCLE_MONITOR_H_ + +#import + +#include "internal/platform/implementation/app_lifecycle_monitor.h" + +#import "internal/platform/implementation/apple/GNCNotificationCenter.h" + +namespace nearby { +namespace apple { + +class AppLifecycleMonitor : public api::AppLifecycleMonitor { + public: + explicit AppLifecycleMonitor(std::function state_updated_callback); + + // Test only. + AppLifecycleMonitor(id notification_center, + std::function state_updated_callback); + ~AppLifecycleMonitor() override; + + private: + id notification_center_ = nil; + id active_observer_ = nil; + id inactive_observer_ = nil; + id background_observer_ = nil; +}; + +} // namespace apple +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_APPLE_APP_LIFECYCLE_MONITOR_H_ diff --git a/internal/platform/implementation/apple/app_lifecycle_monitor.mm b/internal/platform/implementation/apple/app_lifecycle_monitor.mm new file mode 100644 index 00000000..3d89f7ec --- /dev/null +++ b/internal/platform/implementation/apple/app_lifecycle_monitor.mm @@ -0,0 +1,95 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "internal/platform/implementation/apple/app_lifecycle_monitor.h" + +#import + +#include + +#if TARGET_OS_IPHONE +#import +#endif // TARGET_OS_IPHONE + +#import "internal/platform/implementation/apple/GNCNotificationCenter.h" +#import "internal/platform/implementation/apple/Log/GNCLogger.h" + +namespace nearby { +namespace apple { + +AppLifecycleMonitor::AppLifecycleMonitor( + std::function state_updated_callback) + : AppLifecycleMonitor([NSNotificationCenter defaultCenter], std::move(state_updated_callback)) { +} + +AppLifecycleMonitor::AppLifecycleMonitor( + id notification_center, + std::function state_updated_callback) + : api::AppLifecycleMonitor(std::move(state_updated_callback)), + notification_center_(notification_center) { + // Copy the callback to be captured by the blocks below. This avoids capturing `this` in the + // blocks, which would lead to a dangling pointer if `this` is destroyed. The block will hold a + // copy of the callback. + std::function callback = state_updated_callback_; +#if TARGET_OS_IPHONE + // Register for app lifecycle notifications + background_observer_ = + [notification_center_ addObserverForName:UIApplicationDidEnterBackgroundNotification + object:nil + queue:nil + usingBlock:^(NSNotification *notification) { + GNCLoggerDebug(@"AppLifecycleMonitor: Did enter background."); + if (callback) { + callback(AppLifecycleState::kBackground); + } + }]; + + inactive_observer_ = + [notification_center_ addObserverForName:UIApplicationWillResignActiveNotification + object:nil + queue:nil + usingBlock:^(NSNotification *notification) { + GNCLoggerDebug(@"AppLifecycleMonitor: Will enter inactive."); + if (callback) { + callback(AppLifecycleState::kInactive); + } + }]; + + active_observer_ = + [notification_center_ addObserverForName:UIApplicationDidBecomeActiveNotification + object:nil + queue:nil + usingBlock:^(NSNotification *notification) { + GNCLoggerDebug(@"AppLifecycleMonitor: Will enter active."); + if (callback) { + callback(AppLifecycleState::kActive); + } + }]; +#else + GNCLoggerError(@"Not implemented"); +#endif // TARGET_OS_IPHONE +} + +AppLifecycleMonitor::~AppLifecycleMonitor() { +#if TARGET_OS_IPHONE + if (notification_center_) { + [notification_center_ removeObserver:background_observer_]; + [notification_center_ removeObserver:active_observer_]; + [notification_center_ removeObserver:inactive_observer_]; + } +#endif // TARGET_OS_IPHONE +} + +} // namespace apple +} // namespace nearby diff --git a/internal/platform/implementation/apple/platform.mm b/internal/platform/implementation/apple/platform.mm index dd301d36..238013c4 100644 --- a/internal/platform/implementation/apple/platform.mm +++ b/internal/platform/implementation/apple/platform.mm @@ -16,12 +16,14 @@ #import +#include #include #include #include "absl/strings/string_view.h" #include "internal/base/files.h" #import "internal/platform/implementation/apple/Log/GNCLogger.h" +#include "internal/platform/implementation/apple/app_lifecycle_monitor.h" #include "internal/platform/implementation/apple/atomic_boolean.h" #include "internal/platform/implementation/apple/atomic_uint32.h" #include "internal/platform/implementation/apple/awdl.h" @@ -205,6 +207,15 @@ std::unique_ptr ImplementationPlatform::CreateWifiDirectMedium std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { return nullptr; } #endif +std::unique_ptr ImplementationPlatform::CreateAppLifecycleMonitor( + std::function state_updated_callback) { +#if TARGET_OS_IPHONE + return std::make_unique(std::move(state_updated_callback)); +#else + return nullptr; +#endif +} + absl::StatusOr ImplementationPlatform::SendRequest(const WebRequest& requestInfo) { NSURL* url = [NSURL URLWithString:@(requestInfo.url.c_str())]; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; diff --git a/internal/platform/implementation/g3/platform.cc b/internal/platform/implementation/g3/platform.cc index 74292122..6c3a60d7 100644 --- a/internal/platform/implementation/g3/platform.cc +++ b/internal/platform/implementation/g3/platform.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -26,6 +27,7 @@ #include "absl/strings/string_view.h" #include "internal/base/file_path.h" #include "internal/base/files.h" +#include "internal/platform/implementation/app_lifecycle_monitor.h" #include "internal/platform/implementation/atomic_boolean.h" #include "internal/platform/implementation/atomic_reference.h" #include "internal/platform/implementation/awdl.h" @@ -184,8 +186,8 @@ ImplementationPlatform::CreateBluetoothClassicMedium( return std::make_unique(adapter); } -std::unique_ptr -ImplementationPlatform::CreateBleMedium(api::BluetoothAdapter& adapter) { +std::unique_ptr ImplementationPlatform::CreateBleMedium( + api::BluetoothAdapter& adapter) { return std::make_unique( dynamic_cast(adapter)); } @@ -227,6 +229,13 @@ std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { } #endif +std::unique_ptr +ImplementationPlatform::CreateAppLifecycleMonitor( + std::function + state_updated_callback) { + return nullptr; +} + absl::StatusOr ImplementationPlatform::SendRequest( const api::WebRequest& request) { return absl::UnimplementedError(""); diff --git a/internal/platform/implementation/platform.h b/internal/platform/implementation/platform.h index b38aaeb8..55915254 100644 --- a/internal/platform/implementation/platform.h +++ b/internal/platform/implementation/platform.h @@ -15,12 +15,15 @@ #ifndef PLATFORM_API_PLATFORM_H_ #define PLATFORM_API_PLATFORM_H_ +#include #include +#include #include #include #include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "internal/platform/implementation/app_lifecycle_monitor.h" #include "internal/platform/implementation/atomic_boolean.h" #include "internal/platform/implementation/atomic_reference.h" #include "internal/platform/implementation/awdl.h" @@ -138,6 +141,18 @@ class ImplementationPlatform { static std::unique_ptr CreateWebRtcMedium(); #endif +#if defined(NEARBY_CHROMIUM) + static std::unique_ptr CreateAppLifecycleMonitor( + std::function + state_updated_callback) { + return nullptr; + } +#else + static std::unique_ptr CreateAppLifecycleMonitor( + std::function + state_updated_callback); +#endif + // Gets HTTP response from remote server. // // @param request Webrequest @@ -147,7 +162,12 @@ class ImplementationPlatform { // other cases will return absl Status in error. static absl::StatusOr SendRequest(const WebRequest& request); -#ifndef NEARBY_CHROMIUM +#if defined(NEARBY_CHROMIUM) + static std::unique_ptr + CreatePreferencesManager(absl::string_view path) { + return nullptr; + } +#else static std::unique_ptr CreatePreferencesManager(absl::string_view path); #endif diff --git a/internal/platform/implementation/windows/platform.cc b/internal/platform/implementation/windows/platform.cc index c3ebb722..17e98502 100644 --- a/internal/platform/implementation/windows/platform.cc +++ b/internal/platform/implementation/windows/platform.cc @@ -28,11 +28,13 @@ #include #include #include +#include #include #include #include "absl/base/attributes.h" #include "absl/status/statusor.h" +#include "internal/platform/implementation/app_lifecycle_monitor.h" #undef StrCat // Remove the Windows macro definition #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" @@ -290,6 +292,13 @@ std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { return nullptr; } +std::unique_ptr +ImplementationPlatform::CreateAppLifecycleMonitor( + std::function + state_updated_callback) { + return nullptr; +} + absl::StatusOr ImplementationPlatform::SendRequest( const WebRequest& request) { windows::HttpLoader http_loader{request};