|
7 | 7 | //
|
8 | 8 |
|
9 | 9 | import Combine
|
| 10 | +import MullvadLogging |
| 11 | +import MullvadTypes |
| 12 | +import Network |
10 | 13 | import NetworkExtension
|
11 | 14 | import PacketTunnelCore
|
12 | 15 |
|
13 |
| -final class PacketTunnelPathObserver: DefaultPathObserverProtocol, @unchecked Sendable { |
14 |
| - private weak var packetTunnelProvider: NEPacketTunnelProvider? |
15 |
| - private let stateLock = NSLock() |
16 |
| - private var pathUpdatePublisher: AnyCancellable? |
| 16 | +final class PacketTunnelPathObserver: DefaultPathObserverProtocol, Sendable { |
17 | 17 | private let eventQueue: DispatchQueue
|
| 18 | + private let pathMonitor: NWPathMonitor |
| 19 | + nonisolated(unsafe) let logger = Logger(label: "PacketTunnelPathObserver") |
| 20 | + private let stateLock = NSLock() |
18 | 21 |
|
19 |
| - init(packetTunnelProvider: NEPacketTunnelProvider, eventQueue: DispatchQueue) { |
20 |
| - self.packetTunnelProvider = packetTunnelProvider |
21 |
| - self.eventQueue = eventQueue |
| 22 | + nonisolated(unsafe) private var started = false |
| 23 | + |
| 24 | + public var currentPathStatus: Network.NWPath.Status { |
| 25 | + stateLock.withLock { |
| 26 | + pathMonitor.currentPath.status |
| 27 | + } |
22 | 28 | }
|
23 | 29 |
|
24 |
| - var defaultPath: NetworkPath? { |
25 |
| - return packetTunnelProvider?.defaultPath |
| 30 | + init(eventQueue: DispatchQueue) { |
| 31 | + self.eventQueue = eventQueue |
| 32 | + |
| 33 | + pathMonitor = NWPathMonitor(prohibitedInterfaceTypes: [.other]) |
26 | 34 | }
|
27 | 35 |
|
28 |
| - func start(_ body: @escaping @Sendable (NetworkPath) -> Void) { |
| 36 | + func start(_ body: @escaping @Sendable (Network.NWPath.Status) -> Void) { |
29 | 37 | stateLock.withLock {
|
30 |
| - pathUpdatePublisher?.cancel() |
31 |
| - |
32 |
| - // Normally packet tunnel provider should exist throughout the network extension lifetime. |
33 |
| - pathUpdatePublisher = packetTunnelProvider?.publisher(for: \.defaultPath) |
34 |
| - .removeDuplicates(by: { oldPath, newPath in |
35 |
| - oldPath?.status == newPath?.status |
36 |
| - }) |
37 |
| - .throttle(for: .seconds(2), scheduler: eventQueue, latest: true) |
38 |
| - .sink { change in |
39 |
| - if let change { |
40 |
| - body(change) |
41 |
| - } |
42 |
| - } |
| 38 | + guard started == false else { return } |
| 39 | + defer { started = true } |
| 40 | + pathMonitor.pathUpdateHandler = { updatedPath in |
| 41 | + body(updatedPath.status) |
| 42 | + } |
| 43 | + |
| 44 | + pathMonitor.start(queue: eventQueue) |
43 | 45 | }
|
44 | 46 | }
|
45 | 47 |
|
46 | 48 | func stop() {
|
47 | 49 | stateLock.withLock {
|
48 |
| - pathUpdatePublisher?.cancel() |
49 |
| - pathUpdatePublisher = nil |
| 50 | + guard started == true else { return } |
| 51 | + defer { started = false } |
| 52 | + pathMonitor.pathUpdateHandler = nil |
| 53 | + pathMonitor.cancel() |
50 | 54 | }
|
51 | 55 | }
|
52 | 56 | }
|
53 |
| - |
54 |
| -extension NetworkExtension.NWPath: NetworkPath {} |
|
0 commit comments