diff --git a/Session/Calls/Call Management/SessionCallManager.swift b/Session/Calls/Call Management/SessionCallManager.swift index 585cd2ce8..b7df2f0d6 100644 --- a/Session/Calls/Call Management/SessionCallManager.swift +++ b/Session/Calls/Call Management/SessionCallManager.swift @@ -197,7 +197,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol { // Stop all jobs except for message sending and when completed suspend the database JobRunner.stopAndClearPendingJobs(exceptForVariant: .messageSend, using: dependencies) { - LibSession.closeNetworkConnections() + LibSession.suspendNetworkAccess() Storage.suspendDatabaseAccess() Log.flush() } diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index 03c7d2379..1195adc62 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -147,6 +147,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD UNUserNotificationCenter.current().delegate = self Storage.resumeDatabaseAccess() + LibSession.resumeNetworkAccess() // Reset the 'startTime' (since it would be invalid from the last launch) startTime = CACurrentMediaTime() @@ -211,7 +212,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // Stop all jobs except for message sending and when completed suspend the database JobRunner.stopAndClearPendingJobs(exceptForVariant: .messageSend, using: dependencies) { if !self.hasCallOngoing() { - LibSession.closeNetworkConnections() + LibSession.suspendNetworkAccess() Storage.suspendDatabaseAccess() Log.info("[AppDelegate] completed network and database shutdowns.") Log.flush() @@ -281,6 +282,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { Storage.resumeDatabaseAccess() + LibSession.resumeNetworkAccess() // Background tasks only last for a certain amount of time (which can result in a crash and a // prompt appearing for the user), we want to avoid this and need to make sure to suspend the @@ -298,7 +300,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD BackgroundPoller.isValid = false if Singleton.hasAppContext && Singleton.appContext.isInBackground { - LibSession.closeNetworkConnections() + LibSession.suspendNetworkAccess() Storage.suspendDatabaseAccess() Log.flush() } @@ -325,7 +327,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD BackgroundPoller.isValid = false if Singleton.hasAppContext && Singleton.appContext.isInBackground { - LibSession.closeNetworkConnections() + LibSession.suspendNetworkAccess() Storage.suspendDatabaseAccess() Log.flush() } diff --git a/Session/Meta/SessionApp.swift b/Session/Meta/SessionApp.swift index f122436c9..5aad13143 100644 --- a/Session/Meta/SessionApp.swift +++ b/Session/Meta/SessionApp.swift @@ -114,7 +114,7 @@ public struct SessionApp { public static func resetAppData(onReset: (() -> ())? = nil) { LibSession.clearMemoryState() LibSession.clearSnodeCache() - LibSession.closeNetworkConnections() + LibSession.suspendNetworkAccess() Storage.resetAllStorage() ProfileManager.resetProfileStorage() Attachment.resetAttachmentStorage() diff --git a/Session/Notifications/PushRegistrationManager.swift b/Session/Notifications/PushRegistrationManager.swift index 52c817e60..a22e012e6 100644 --- a/Session/Notifications/PushRegistrationManager.swift +++ b/Session/Notifications/PushRegistrationManager.swift @@ -287,6 +287,7 @@ public enum PushRegistrationError: Error { } Storage.resumeDatabaseAccess() + LibSession.resumeNetworkAccess() let maybeCall: SessionCall? = Storage.shared.write { db in let messageInfo: CallMessage.MessageInfo = CallMessage.MessageInfo( diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift index 3a54f3ee4..440edad3c 100644 --- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift +++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift @@ -51,6 +51,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension // Perform main setup Storage.resumeDatabaseAccess() + LibSession.resumeNetworkAccess() DispatchQueue.main.sync { self.setUpIfNecessary() { } } // Handle the push notification @@ -349,7 +350,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension .map { NSNumber(value: $0) } .defaulting(to: NSNumber(value: 0)) Log.info("Complete silently.") - LibSession.closeNetworkConnections() + LibSession.suspendNetworkAccess() Storage.suspendDatabaseAccess() Log.flush() @@ -416,7 +417,7 @@ public final class NotificationServiceExtension: UNNotificationServiceExtension private func handleFailure(for content: UNMutableNotificationContent, error: NotificationError) { Log.error("Show generic failure message due to error: \(error).") - LibSession.closeNetworkConnections() + LibSession.suspendNetworkAccess() Storage.suspendDatabaseAccess() Log.flush() diff --git a/SessionShareExtension/ThreadPickerVC.swift b/SessionShareExtension/ThreadPickerVC.swift index 21aaa460e..366532a28 100644 --- a/SessionShareExtension/ThreadPickerVC.swift +++ b/SessionShareExtension/ThreadPickerVC.swift @@ -100,7 +100,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView // When the thread picker disappears it means the user has left the screen (this will be called // whether the user has sent the message or cancelled sending) - LibSession.closeNetworkConnections() + LibSession.suspendNetworkAccess() Storage.suspendDatabaseAccess() Log.flush() } @@ -229,6 +229,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView ModalActivityIndicatorViewController.present(fromViewController: shareNavController!, canCancel: false, message: "vc_share_sending_message".localized()) { activityIndicator in Storage.resumeDatabaseAccess() + LibSession.resumeNetworkAccess() let swarmPublicKey: String = { switch threadVariant { @@ -314,7 +315,7 @@ final class ThreadPickerVC: UIViewController, UITableViewDataSource, UITableView .receive(on: DispatchQueue.main) .sinkUntilComplete( receiveCompletion: { [weak self] result in - LibSession.closeNetworkConnections() + LibSession.suspendNetworkAccess() Storage.suspendDatabaseAccess() Log.flush() activityIndicator.dismiss { } diff --git a/SessionSnodeKit/LibSession/LibSession+Networking.swift b/SessionSnodeKit/LibSession/LibSession+Networking.swift index a5ee46e34..1e6e34b1b 100644 --- a/SessionSnodeKit/LibSession/LibSession+Networking.swift +++ b/SessionSnodeKit/LibSession/LibSession+Networking.swift @@ -12,6 +12,7 @@ import SessionUtilitiesKit public extension LibSession { private static var networkCache: Atomic?> = Atomic(nil) private static var snodeCachePath: String { "\(OWSFileSystem.appSharedDataDirectoryPath())/snodeCache" } + private static var isSuspended: Atomic = Atomic(false) private static var lastPaths: Atomic<[[Snode]]> = Atomic([]) private static var lastNetworkStatus: Atomic = Atomic(.unknown) private static var pathsChangedCallbacks: Atomic<[UUID: ([[Snode]], UUID) -> ()]> = Atomic([:]) @@ -114,12 +115,18 @@ public extension LibSession { pathsChangedCallbacks.mutate { $0.removeValue(forKey: callbackId) } } - static func closeNetworkConnections() { + static func suspendNetworkAccess() { + isSuspended.mutate { $0 = true } + guard let network: UnsafeMutablePointer = networkCache.wrappedValue else { return } network_close_connections(network) } + static func resumeNetworkAccess() { + isSuspended.mutate { $0 = false } + } + static func clearSnodeCache() { guard let network: UnsafeMutablePointer = networkCache.wrappedValue else { return } @@ -329,6 +336,11 @@ public extension LibSession { // MARK: - Internal Functions private static func getOrCreateNetwork() -> AnyPublisher?, Error> { + guard !isSuspended.wrappedValue else { + Log.warn("[LibSession] Attempted to access suspended network.") + return Fail(error: NetworkError.suspended).eraseToAnyPublisher() + } + guard networkCache.wrappedValue == nil else { return Just(networkCache.wrappedValue) .setFailureType(to: Error.self) diff --git a/SessionUtilitiesKit/Networking/NetworkError.swift b/SessionUtilitiesKit/Networking/NetworkError.swift index 948b16d7a..721c40c83 100644 --- a/SessionUtilitiesKit/Networking/NetworkError.swift +++ b/SessionUtilitiesKit/Networking/NetworkError.swift @@ -15,6 +15,7 @@ public enum NetworkError: LocalizedError, Equatable { case badRequest(error: String, rawData: Data?) case requestFailed(error: String, rawData: Data?) case timeout + case suspended case unknown public var errorDescription: String? { @@ -27,6 +28,7 @@ public enum NetworkError: LocalizedError, Equatable { case .unauthorised: return "Unauthorised (Failed to verify the signature)." case .badRequest(let error, _), .requestFailed(let error, _): return error case .timeout: return "The request timed out." + case .suspended: return "Network requests are suspended." case .unknown: return "An unknown error occurred." } }