@mentra/glasses-media 3.4.0-dev.1209 → 3.4.0-dev.1211

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.
@@ -0,0 +1,26 @@
1
+ import Foundation
2
+
3
+ /// When to ask iOS again to join the glasses hotspot.
4
+ ///
5
+ /// iOS accepts a hotspot configuration and then makes one short directed scan for it. When that
6
+ /// scan misses the glasses' beacon, iOS shows "Unable to join the network", stays on the current
7
+ /// Wi-Fi and still reports the configuration as applied. Only the phone's current network shows
8
+ /// that the join failed, so a request that has not put the phone on the hotspot after a few
9
+ /// seconds is made again, a bounded number of times, within the join's overall deadline.
10
+ public struct HotspotReapplyPolicy {
11
+ /// Polls of the current network, half a second apart, before a request counts as missed.
12
+ public let pollsBeforeReapply: Int
13
+ /// Requests in one join, the first included.
14
+ public let maxApplies: Int
15
+
16
+ public init(pollsBeforeReapply: Int = 16, maxApplies: Int = 3) {
17
+ self.pollsBeforeReapply = pollsBeforeReapply
18
+ self.maxApplies = maxApplies
19
+ }
20
+
21
+ /// Whether to request the join again, with the phone not on the hotspot after `pollsSinceApply`
22
+ /// polls of the `applies`-th request.
23
+ public func shouldReapply(applies: Int, pollsSinceApply: Int) -> Bool {
24
+ applies < maxApplies && pollsSinceApply >= pollsBeforeReapply
25
+ }
26
+ }
@@ -17,6 +17,8 @@ public final class GlassesHotspotNetwork {
17
17
  private var generation = 0
18
18
  private var applying = false
19
19
  private var ownsConfiguration = false
20
+ private var applies = 0
21
+ private let reapplyPolicy = HotspotReapplyPolicy()
20
22
  private var cancelled = false
21
23
  private var joinReply: ((Result<String, Error>) -> Void)?
22
24
  private var leaveCompletion: HotspotLeaveCompletion?
@@ -63,6 +65,7 @@ public final class GlassesHotspotNetwork {
63
65
  self.cancelled = false
64
66
  self.applying = false
65
67
  self.ownsConfiguration = false
68
+ self.applies = 0
66
69
  self.joinReply = completion
67
70
  self.startInterfaceMonitors(generation: gen)
68
71
  if ProcessInfo.processInfo.isiOSAppOnMac {
@@ -324,12 +327,13 @@ public final class GlassesHotspotNetwork {
324
327
  }
325
328
  }
326
329
 
327
- private func applyConfiguration(ssid: String, passphrase: String, generation gen: Int) {
330
+ private func applyConfiguration(ssid: String, passphrase: String, generation gen: Int, remaining: Int = 60) {
328
331
  let config = NEHotspotConfiguration(ssid: ssid, passphrase: passphrase, isWEP: false)
329
332
  config.joinOnce = false
330
333
  applying = true
331
334
  ownsConfiguration = true
332
- logger.info("HOTSPOT_JOIN apply_start ios_on_mac=\(ProcessInfo.processInfo.isiOSAppOnMac)")
335
+ applies += 1
336
+ logger.info("HOTSPOT_JOIN apply_start attempt=\(self.applies) ios_on_mac=\(ProcessInfo.processInfo.isiOSAppOnMac)")
333
337
  NEHotspotConfigurationManager.shared.apply(config) { error in
334
338
  self.queue.async {
335
339
  guard gen == self.generation else { return }
@@ -347,12 +351,12 @@ public final class GlassesHotspotNetwork {
347
351
  }
348
352
  }
349
353
  self.logger.info("HOTSPOT_JOIN apply_accepted")
350
- self.waitForAddress(ssid: ssid, generation: gen, remaining: 60)
354
+ self.waitForAddress(ssid: ssid, passphrase: passphrase, generation: gen, remaining: remaining, pollsSinceApply: 0)
351
355
  }
352
356
  }
353
357
  }
354
358
 
355
- private func waitForAddress(ssid: String, generation gen: Int, remaining: Int) {
359
+ private func waitForAddress(ssid: String, passphrase: String, generation gen: Int, remaining: Int, pollsSinceApply: Int) {
356
360
  guard gen == generation, !cancelled else { return }
357
361
  NEHotspotNetwork.fetchCurrent { [weak self] network in
358
362
  self?.queue.async {
@@ -361,8 +365,15 @@ public final class GlassesHotspotNetwork {
361
365
  if network?.ssid == ssid, let selected = self.selectBinding() {
362
366
  self.bind(selected)
363
367
  self.finishAssociation(address: selected.address, generation: gen)
368
+ } else if remaining > 0, network?.ssid != ssid,
369
+ self.reapplyPolicy.shouldReapply(applies: self.applies, pollsSinceApply: pollsSinceApply) {
370
+ // iOS reports the request applied even when its one scan missed the hotspot.
371
+ self.logger.info("HOTSPOT_JOIN reapply after_polls=\(pollsSinceApply)")
372
+ self.applyConfiguration(ssid: ssid, passphrase: passphrase, generation: gen, remaining: remaining)
364
373
  } else if remaining > 0 {
365
- self.queue.asyncAfter(deadline: .now() + 0.5) { self.waitForAddress(ssid: ssid, generation: gen, remaining: remaining - 1) }
374
+ self.queue.asyncAfter(deadline: .now() + 0.5) {
375
+ self.waitForAddress(ssid: ssid, passphrase: passphrase, generation: gen, remaining: remaining - 1, pollsSinceApply: pollsSinceApply + 1)
376
+ }
366
377
  } else {
367
378
  let association = network == nil ? "unavailable" : (network?.ssid == ssid ? "matched" : "different")
368
379
  self.finishJoin(.failure(LocalMediaError("Glasses hotspot has no verified Wi-Fi address (SSID=\(association), \(self.wifiDiagnostic()))")))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/glasses-media",
3
- "version": "3.4.0-dev.1209",
3
+ "version": "3.4.0-dev.1211",
4
4
  "description": "Shared native glasses media receivers and local network transport",
5
5
  "license": "MIT",
6
6
  "author": "Mentra Community",