// TunnelActivationError
case TunnelActivationError.tunnelActivationFailed:
return ("Activation failure", "The tunnel could not be activated due to an internal error")
- case TunnelActivationError.attemptingActivationWhenAnotherTunnelIsBusy(let otherTunnelStatus):
- let statusString: String = {
- switch (otherTunnelStatus) {
- case .active: fallthrough
- case .reasserting: fallthrough
- case .restarting:
- return "active"
- case .activating: fallthrough
- case .deactivating:
- return "being deactivated"
- case .inactive:
- fatalError()
- }
- }()
- return ("Activation failure", "Another tunnel is currently \(statusString)")
default:
os_log("ErrorPresenter: Error not presented: %{public}@", log: OSLog.default, type: .error, "\(error)")
enum TunnelActivationError: Error {
case tunnelActivationFailed
- case attemptingActivationWhenAnotherTunnelIsBusy(otherTunnelStatus: TunnelStatus)
case attemptingActivationWhenTunnelIsNotInactive
case attemptingDeactivationWhenTunnelIsInactive
}
completionHandler(TunnelActivationError.attemptingActivationWhenTunnelIsNotInactive)
return
}
- for t in tunnels {
- if t.status != .inactive {
- completionHandler(TunnelActivationError.attemptingActivationWhenAnotherTunnelIsBusy(otherTunnelStatus: t.status))
- return
+ if let tunnelInOperation = tunnels.first(where: { $0.status != .inactive }) {
+ tunnel.status = .waiting
+ tunnelInOperation.onDeactivationComplete = {
+ tunnel.startActivation(completionHandler: completionHandler)
}
+ startDeactivation(of: tunnelInOperation)
+ } else {
+ tunnel.startActivation(completionHandler: completionHandler)
}
- tunnel.startActivation(completionHandler: completionHandler)
}
func startDeactivation(of tunnel: TunnelContainer) {
@objc dynamic var name: String
@objc dynamic var status: TunnelStatus
+ var onDeactivationComplete: (() -> Void)?
+
fileprivate let tunnelProvider: NETunnelProviderManager
private var statusObservationToken: AnyObject?
}
fileprivate func startActivation(completionHandler: @escaping (Error?) -> Void) {
- assert(status == .inactive || status == .restarting)
+ assert(status == .inactive || status == .restarting || status == .waiting)
guard let tunnelConfiguration = tunnelConfiguration() else { fatalError() }
+ onDeactivationComplete = nil
startActivation(tunnelConfiguration: tunnelConfiguration,
completionHandler: completionHandler)
}
s.status = TunnelStatus(from: connection.status)
if (s.status == .inactive) {
s.statusObservationToken = nil
+ s.onDeactivationComplete?()
+ s.onDeactivationComplete = nil
}
}
}
case reasserting // Not a possible state at present
case restarting // Restarting tunnel (done after saving modifications to an active tunnel)
+ case waiting // Waiting for another tunnel to be brought down
init(from vpnStatus: NEVPNStatus) {
switch (vpnStatus) {