summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2022-12-19 11:32:01 +0100
committerAndrej Mihajlov <and@mullvad.net>2022-12-19 13:35:58 +0100
commitbb643b559ed24e582714124d39b9e47513b1173e (patch)
treee92ba106f6415a343f184f9d0bea67b292887f46
parentcce928896f27319cffc65db0541bd5ec3672fc7a (diff)
downloadmullvadvpn-bb643b559ed24e582714124d39b9e47513b1173e.tar.xz
mullvadvpn-bb643b559ed24e582714124d39b9e47513b1173e.zip
Reconnect tunnel after migration from AppDelegate
-rw-r--r--ios/MullvadVPN/AppDelegate.swift131
-rw-r--r--ios/MullvadVPN/SettingsManager/SettingsManager.swift72
-rw-r--r--ios/MullvadVPN/TunnelManager/TunnelManager.swift11
3 files changed, 120 insertions, 94 deletions
diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift
index dfdee88d7e..f4307d0f28 100644
--- a/ios/MullvadVPN/AppDelegate.swift
+++ b/ios/MullvadVPN/AppDelegate.swift
@@ -113,61 +113,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
setupNotificationHandler()
addApplicationNotifications(application: application)
- let loadTunnelsOperation = AsyncBlockOperation(dispatchQueue: .main) { operation in
- self.tunnelStore.loadPersistentTunnels { error in
- if let error = error {
- self.logger.error(
- error: error,
- message: "Failed to load persistent tunnels."
- )
- }
- operation.finish()
- }
- }
-
- let migrateSettingsOperation = AsyncBlockOperation(dispatchQueue: .main) { operation in
- SettingsManager.migrateStore(with: self.proxyFactory) { error in
- guard let error = error else {
- operation.finish()
- return
- }
-
- guard let migrationUIHandler = application.connectedScenes.compactMap({ scene in
- return scene.delegate as? SettingsMigrationUIHandler
- }).first else {
- operation.finish()
- return
- }
-
- migrationUIHandler.showMigrationError(error) {
- operation.finish()
- }
- }
- }
- migrateSettingsOperation.addDependency(loadTunnelsOperation)
-
- let loadTunnelConfigurationOperation =
- AsyncBlockOperation(dispatchQueue: .main) { operation in
- self.tunnelManager.loadConfiguration { error in
- // TODO: avoid throwing fatal error and show the problem report UI instead.
- if let error = error {
- fatalError(error.localizedDescription)
- }
-
- self.logger.debug("Finished initialization.")
-
- NotificationManager.shared.updateNotifications()
- self.storePaymentManager.startPaymentQueueMonitoring()
-
- operation.finish()
- }
- }
- loadTunnelConfigurationOperation.addDependency(migrateSettingsOperation)
-
- operationQueue.addOperations(
- [loadTunnelsOperation, migrateSettingsOperation, loadTunnelConfigurationOperation],
- waitUntilFinished: false
- )
+ startInitialization(application: application)
return true
}
@@ -405,6 +351,81 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
UNUserNotificationCenter.current().delegate = self
}
+ private func startInitialization(application: UIApplication) {
+ let loadTunnelStoreOperation = AsyncBlockOperation(dispatchQueue: .main) { operation in
+ self.tunnelStore.loadPersistentTunnels { error in
+ if let error = error {
+ self.logger.error(
+ error: error,
+ message: "Failed to load persistent tunnels."
+ )
+ }
+ operation.finish()
+ }
+ }
+
+ let migrateSettingsOperation = ResultBlockOperation<SettingsMigrationResult, Error>(
+ dispatchQueue: .main
+ ) { operation in
+ SettingsManager.migrateStore(with: self.proxyFactory) { migrationResult in
+ let finishHandler = {
+ operation.finish(completion: .success(migrationResult))
+ }
+
+ guard case let .failure(error) = migrationResult,
+ let migrationUIHandler = application.connectedScenes.compactMap({ scene in
+ return scene.delegate as? SettingsMigrationUIHandler
+ }).first
+ else {
+ finishHandler()
+ return
+ }
+
+ migrationUIHandler.showMigrationError(error, completionHandler: finishHandler)
+ }
+ }
+ migrateSettingsOperation.addDependency(loadTunnelStoreOperation)
+
+ let initTunnelManagerOperation = AsyncBlockOperation(dispatchQueue: .main) { operation in
+ self.tunnelManager.loadConfiguration { error in
+ // TODO: avoid throwing fatal error and show the problem report UI instead.
+ if let error = error {
+ fatalError(error.localizedDescription)
+ }
+
+ self.logger.debug("Finished initialization.")
+
+ NotificationManager.shared.updateNotifications()
+ self.storePaymentManager.startPaymentQueueMonitoring()
+
+ operation.finish()
+ }
+ }
+ initTunnelManagerOperation.addDependency(migrateSettingsOperation)
+
+ let reconnectTunnelOperation = TransformOperation<SettingsMigrationResult, Void, Error>(
+ dispatchQueue: .main
+ ) { migrationResult in
+ if case .success = migrationResult {
+ self.logger.debug("Reconnect the tunnel after settings migration.")
+
+ self.tunnelManager.reconnectTunnel(selectNewRelay: true)
+ }
+ }
+ reconnectTunnelOperation.inject(from: migrateSettingsOperation)
+ reconnectTunnelOperation.addDependency(initTunnelManagerOperation)
+
+ operationQueue.addOperations(
+ [
+ loadTunnelStoreOperation,
+ migrateSettingsOperation,
+ initTunnelManagerOperation,
+ reconnectTunnelOperation,
+ ],
+ waitUntilFinished: false
+ )
+ }
+
// MARK: - StorePaymentManagerDelegate
func storePaymentManager(
diff --git a/ios/MullvadVPN/SettingsManager/SettingsManager.swift b/ios/MullvadVPN/SettingsManager/SettingsManager.swift
index 2940937ae4..06ae10f1a6 100644
--- a/ios/MullvadVPN/SettingsManager/SettingsManager.swift
+++ b/ios/MullvadVPN/SettingsManager/SettingsManager.swift
@@ -15,6 +15,17 @@ private let keychainServiceName = "Mullvad VPN"
private let accountTokenKey = "accountToken"
private let accountExpiryKey = "accountExpiry"
+enum SettingsMigrationResult {
+ /// Nothing to migrate.
+ case nothing
+
+ /// Successfully performed migration.
+ case success
+
+ /// Failure when migrating store.
+ case failure(Error)
+}
+
enum SettingsManager {
private static let logger = Logger(label: "SettingsManager")
@@ -110,31 +121,35 @@ enum SettingsManager {
/// Migrate settings store if needed.
///
- /// The error returned in `completion` handler is set to `nil` upon success or when no
- /// migration is not needed.
- ///
/// The following types of error are expected to be returned by this method:
/// `SettingsMigrationError`, `UnsupportedSettingsVersionError`, `ReadSettingsVersionError`.
static func migrateStore(
with restFactory: REST.ProxyFactory,
- completion: @escaping (Error?) -> Void
+ completion: @escaping (SettingsMigrationResult) -> Void
) {
- let handleCompletion = { (error: Error?) in
+ let handleCompletion = { (result: SettingsMigrationResult) in
// Reset store upon failure to migrate settings.
- if error != nil {
+ if case .failure = result {
self.resetStore()
}
- completion(error)
+ completion(result)
}
if let legacySettings = readLegacySettings() {
migrateLegacySettings(
restFactory: restFactory,
- legacySettings: legacySettings,
- completion: handleCompletion
- )
+ legacySettings: legacySettings
+ ) { error in
+ handleCompletion(error.map { .failure($0) } ?? .success)
+ }
} else {
- migrateModernSettings(completion: handleCompletion)
+ do {
+ try checkLatestSettingsVersion()
+
+ handleCompletion(.nothing)
+ } catch {
+ handleCompletion(.failure(error))
+ }
}
}
@@ -171,29 +186,30 @@ enum SettingsManager {
}
}
- private static func migrateModernSettings(completion: @escaping (Error?) -> Void) {
+ private static func checkLatestSettingsVersion() throws {
+ let settingsVersion: Int
do {
let parser = makeParser()
let settingsData = try store.read(key: .settings)
- let settingsVersion = try parser.parseVersion(data: settingsData)
-
- if settingsVersion != SchemaVersion.current.rawValue {
- let error = UnsupportedSettingsVersionError(
- storedVersion: settingsVersion,
- currentVersion: SchemaVersion.current
- )
-
- logger.error(error: error, message: "Encountered an unknown version.")
-
- completion(error)
- } else {
- completion(nil)
- }
+ settingsVersion = try parser.parseVersion(data: settingsData)
} catch .itemNotFound as KeychainError {
- completion(nil)
+ return
} catch {
- completion(ReadSettingsVersionError(underlyingError: error))
+ throw ReadSettingsVersionError(underlyingError: error)
}
+
+ guard settingsVersion != SchemaVersion.current.rawValue else {
+ return
+ }
+
+ let error = UnsupportedSettingsVersionError(
+ storedVersion: settingsVersion,
+ currentVersion: SchemaVersion.current
+ )
+
+ logger.error(error: error, message: "Encountered an unknown version.")
+
+ throw error
}
/// Removes all legacy settings, device state and tunnel settings but keeps the last used
diff --git a/ios/MullvadVPN/TunnelManager/TunnelManager.swift b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
index c117dee394..3854f3ca54 100644
--- a/ios/MullvadVPN/TunnelManager/TunnelManager.swift
+++ b/ios/MullvadVPN/TunnelManager/TunnelManager.swift
@@ -84,9 +84,6 @@ final class TunnelManager: StorePaymentObserver {
/// Last processed device check identifier.
private var lastDeviceCheckIdentifier: UUID?
- /// Flag indicating tunnel reconnected after settings migration.
- private var reconnectedTunnelAfterMigration = false
-
// MARK: - Initialization
init(
@@ -697,14 +694,6 @@ final class TunnelManager: StorePaymentObserver {
// while the tunnel process is trying to connect.
startPollingTunnelStatus(interval: establishingTunnelStatusPollInterval)
- if newTunnelStatus.packetTunnelStatus.lastErrors.contains(.readConfiguration),
- !reconnectedTunnelAfterMigration
- {
- reconnectTunnel(selectNewRelay: true)
-
- reconnectedTunnelAfterMigration = true
- }
-
case .connected, .waitingForConnectivity:
// Start polling tunnel status to keep connectivity status up to date.
startPollingTunnelStatus(interval: establishedTunnelStatusPollInterval)