@mentra/glasses-media 3.2.1-dev.294 → 3.2.1-dev.297
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -39,6 +39,14 @@ public enum LocalMediaPolicy {
|
|
|
39
39
|
return bytes[3] > 0 && bytes[3] < 255
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/// A Mac may join through its host network controls. Reuse that connection only
|
|
43
|
+
/// after both its SSID and DHCP address match the hotspot advertised over BLE.
|
|
44
|
+
public static func canReuseHotspot(requestedSSID: String, currentSSID: String?, address: String?, gateway: String?) -> Bool {
|
|
45
|
+
guard !requestedSSID.isEmpty, requestedSSID == currentSSID,
|
|
46
|
+
let address, let gateway else { return false }
|
|
47
|
+
return isHotspotClientAddress(address, gateway: gateway)
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
/// Keep only concrete host candidates on the local link. Never rewrite a cellular socket's
|
|
43
51
|
/// advertised address: a candidate must already belong to the interface it claims.
|
|
44
52
|
public static func localSdp(_ sdp: String, address: String, answer: Bool) throws -> String {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#if MENTRA_E2E
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
/// Test-build dependency input, not a production network discovery mechanism.
|
|
5
|
+
/// The Mac harness owns association and verifies the USB fixture, gateway MAC,
|
|
6
|
+
/// DHCP address, local health and Ethernet before launching this process.
|
|
7
|
+
public struct MacE2EHotspotLease: Decodable {
|
|
8
|
+
public let ssid: String
|
|
9
|
+
public let gateway: String
|
|
10
|
+
public let address: String
|
|
11
|
+
public let issuedAt: TimeInterval
|
|
12
|
+
|
|
13
|
+
public func matches(ssid: String, gateway: String?, address: String?, now: TimeInterval) -> Bool {
|
|
14
|
+
let age = now - issuedAt
|
|
15
|
+
return age >= 0 && age <= 300 && self.gateway == gateway && self.address == address &&
|
|
16
|
+
LocalMediaPolicy.canReuseHotspot(requestedSSID: ssid, currentSSID: self.ssid,
|
|
17
|
+
address: address, gateway: gateway)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
#endif
|
|
@@ -14,11 +14,15 @@ public final class GlassesHotspotNetwork {
|
|
|
14
14
|
private var gatewayAddress: String?
|
|
15
15
|
private var generation = 0
|
|
16
16
|
private var applying = false
|
|
17
|
+
private var ownsConfiguration = false
|
|
17
18
|
private var cancelled = false
|
|
18
19
|
private var joinReply: ((Result<String, Error>) -> Void)?
|
|
19
20
|
private var leaveReplies: [() -> Void] = []
|
|
20
21
|
private var monitor: NWPathMonitor?
|
|
21
22
|
private var localAccess: LocalNetworkAccessRequest?
|
|
23
|
+
#if MENTRA_E2E
|
|
24
|
+
private var testLeaseConsumed = false
|
|
25
|
+
#endif
|
|
22
26
|
public var onLost: ((String) -> Void)?
|
|
23
27
|
public var onPermissionRequired: (() -> Void)?
|
|
24
28
|
public init() {}
|
|
@@ -35,8 +39,49 @@ public final class GlassesHotspotNetwork {
|
|
|
35
39
|
self.lastHotspotSSID = ssid
|
|
36
40
|
self.gatewayAddress = gateway
|
|
37
41
|
self.cancelled = false
|
|
42
|
+
self.applying = false
|
|
43
|
+
self.ownsConfiguration = false
|
|
38
44
|
self.joinReply = completion
|
|
39
|
-
|
|
45
|
+
if ProcessInfo.processInfo.isiOSAppOnMac {
|
|
46
|
+
#if MENTRA_E2E
|
|
47
|
+
if let raw = ProcessInfo.processInfo.environment["MENTRA_E2E_PREJOINED_HOTSPOT"] {
|
|
48
|
+
let address = Self.wifiAddress()
|
|
49
|
+
guard !self.testLeaseConsumed,
|
|
50
|
+
let lease = try? JSONDecoder().decode(MacE2EHotspotLease.self, from: Data(raw.utf8)),
|
|
51
|
+
lease.matches(ssid: ssid, gateway: gateway, address: address, now: Date().timeIntervalSince1970),
|
|
52
|
+
let address
|
|
53
|
+
else {
|
|
54
|
+
self.finishJoin(.failure(LocalMediaError("Mac E2E network lease is invalid, expired or already consumed")))
|
|
55
|
+
self.finishLeave()
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
self.testLeaseConsumed = true
|
|
59
|
+
self.localAddress = address
|
|
60
|
+
self.logger.notice("HOTSPOT_JOIN test_harness_connection native_association_untested")
|
|
61
|
+
self.startMonitor(generation: gen)
|
|
62
|
+
self.waitForLocalAccess(address: address, generation: gen)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
#endif
|
|
66
|
+
NEHotspotNetwork.fetchCurrent { network in
|
|
67
|
+
self.queue.async {
|
|
68
|
+
guard gen == self.generation, !self.cancelled else { return }
|
|
69
|
+
let address = Self.wifiAddress()
|
|
70
|
+
if let address, LocalMediaPolicy.canReuseHotspot(requestedSSID: ssid, currentSSID: network?.ssid,
|
|
71
|
+
address: address, gateway: gateway)
|
|
72
|
+
{
|
|
73
|
+
self.localAddress = address
|
|
74
|
+
self.logger.info("HOTSPOT_JOIN reuse_verified_macos_connection")
|
|
75
|
+
self.startMonitor(generation: gen)
|
|
76
|
+
self.waitForLocalAccess(address: address, generation: gen)
|
|
77
|
+
} else {
|
|
78
|
+
self.applyConfiguration(ssid: ssid, passphrase: passphrase, generation: gen)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
self.applyConfiguration(ssid: ssid, passphrase: passphrase, generation: gen)
|
|
84
|
+
}
|
|
40
85
|
self.queue.asyncAfter(deadline: .now() + 60) {
|
|
41
86
|
// Once associated, the user may still be reading the Local Network alert.
|
|
42
87
|
// Its response time is not a hotspot association timeout.
|
|
@@ -45,7 +90,7 @@ public final class GlassesHotspotNetwork {
|
|
|
45
90
|
self.finishJoin(.failure(LocalMediaError("Hotspot join timed out")))
|
|
46
91
|
// apply() cannot be cancelled. Retain the reservation until its callback and remove the
|
|
47
92
|
// late configuration before another call is permitted to acquire the network.
|
|
48
|
-
NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid)
|
|
93
|
+
if self.ownsConfiguration { NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid) }
|
|
49
94
|
if !self.applying { self.finishLeave() }
|
|
50
95
|
}
|
|
51
96
|
}
|
|
@@ -56,7 +101,7 @@ public final class GlassesHotspotNetwork {
|
|
|
56
101
|
self.cancelled = true
|
|
57
102
|
self.leaveReplies.append(completion)
|
|
58
103
|
self.finishJoin(.failure(LocalMediaError("Hotspot join cancelled")))
|
|
59
|
-
if let ssid = self.ssid { NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid) }
|
|
104
|
+
if let ssid = self.ssid, self.ownsConfiguration { NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid) }
|
|
60
105
|
if !self.applying { self.finishLeave() }
|
|
61
106
|
}
|
|
62
107
|
}
|
|
@@ -147,6 +192,7 @@ public final class GlassesHotspotNetwork {
|
|
|
147
192
|
let config = NEHotspotConfiguration(ssid: ssid, passphrase: passphrase, isWEP: false)
|
|
148
193
|
config.joinOnce = false
|
|
149
194
|
applying = true
|
|
195
|
+
ownsConfiguration = true
|
|
150
196
|
logger.info("HOTSPOT_JOIN apply_start ios_on_mac=\(ProcessInfo.processInfo.isiOSAppOnMac)")
|
|
151
197
|
NEHotspotConfigurationManager.shared.apply(config) { error in
|
|
152
198
|
self.queue.async {
|
|
@@ -236,7 +282,8 @@ public final class GlassesHotspotNetwork {
|
|
|
236
282
|
generation += 1
|
|
237
283
|
localAccess?.cancel(); localAccess = nil
|
|
238
284
|
monitor?.cancel(); monitor = nil
|
|
239
|
-
if let ssid { NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid) }
|
|
285
|
+
if let ssid, ownsConfiguration { NEHotspotConfigurationManager.shared.removeConfiguration(forSSID: ssid) }
|
|
286
|
+
ownsConfiguration = false
|
|
240
287
|
ssid = nil
|
|
241
288
|
localAddress = nil
|
|
242
289
|
gatewayAddress = nil
|