@mentra/glasses-media 3.4.0-dev.971 → 3.4.0-dev.975

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.
@@ -17,12 +17,13 @@ final class MacHotspotAssociation {
17
17
  init(deadline: TimeInterval = ProcessInfo.processInfo.systemUptime + 60,
18
18
  now: @escaping () -> TimeInterval = { ProcessInfo.processInfo.systemUptime },
19
19
  pause: ((TimeInterval) -> Void)? = nil,
20
- makeInterface: @escaping () throws -> MacWifiInterface = { try PublicCoreWlanInterface() })
20
+ makeInterface: (() throws -> MacWifiInterface)? = nil,
21
+ authorizationFacts: MacHotspotAuthorizationFacts? = nil)
21
22
  {
22
23
  self.deadline = deadline
23
24
  self.now = now
24
25
  self.pause = pause
25
- self.makeInterface = makeInterface
26
+ self.makeInterface = makeInterface ?? { try PublicCoreWlanInterface(authorizationFacts: authorizationFacts) }
26
27
  }
27
28
 
28
29
  func associate(ssid: String, password: String) throws -> String {
@@ -129,9 +130,11 @@ enum MacWifiInvocation {
129
130
  private final class PublicCoreWlanInterface: MacWifiInterface {
130
131
  private let bundle: Bundle
131
132
  private let interface: NSObject
133
+ private let authorizationFacts: MacHotspotAuthorizationFacts
132
134
  private typealias Disassociate = @convention(c) (AnyObject, Selector) -> Void
133
135
 
134
- init() throws {
136
+ init(authorizationFacts: MacHotspotAuthorizationFacts? = nil) throws {
137
+ self.authorizationFacts = authorizationFacts ?? MacHotspotAuthorizationFacts()
135
138
  guard let bundle = Bundle(path: "/System/Library/Frameworks/CoreWLAN.framework") else {
136
139
  throw LocalMediaError("Mac Wi-Fi framework is unavailable")
137
140
  }
@@ -161,7 +164,8 @@ private final class PublicCoreWlanInterface: MacWifiInterface {
161
164
  func currentSSID() throws -> String? {
162
165
  let authorization = CLLocationManager.authorizationStatus()
163
166
  guard MacHotspotAuthorization.isAuthorized(authorization) else {
164
- throw LocalMediaError("Location access is required to verify the current Mac Wi-Fi network")
167
+ throw authorizationFacts.failure("Location access is required to verify the current Mac Wi-Fi network", raw: authorization.rawValue,
168
+ getter: .staticSSID, foreground: MacHotspotAuthorizationFacts.foreground())
165
169
  }
166
170
  guard let data = interface.perform(NSSelectorFromString("ssidData"))?.takeUnretainedValue() as? Data else { return nil }
167
171
  return String(data: data, encoding: .utf8)
@@ -7,6 +7,12 @@ final class MacHotspotAuthorization: NSObject, CLLocationManagerDelegate {
7
7
  private var manager: CLLocationManager?
8
8
  private var completion: ((Result<Void, Error>) -> Void)?
9
9
  private var finished = false
10
+ private let facts: MacHotspotAuthorizationFacts
11
+
12
+ init(facts: MacHotspotAuthorizationFacts = MacHotspotAuthorizationFacts()) {
13
+ self.facts = facts
14
+ super.init()
15
+ }
10
16
 
11
17
  func start(completion: @escaping (Result<Void, Error>) -> Void) {
12
18
  DispatchQueue.main.async {
@@ -15,7 +21,7 @@ final class MacHotspotAuthorization: NSObject, CLLocationManagerDelegate {
15
21
  let manager = CLLocationManager()
16
22
  self.manager = manager
17
23
  manager.delegate = self
18
- self.handle(manager.authorizationStatus)
24
+ self.handle(manager.authorizationStatus, getter: .instanceInitial)
19
25
  if manager.authorizationStatus == .notDetermined { manager.requestWhenInUseAuthorization() }
20
26
  }
21
27
  }
@@ -25,7 +31,7 @@ final class MacHotspotAuthorization: NSObject, CLLocationManagerDelegate {
25
31
  }
26
32
 
27
33
  func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
28
- handle(manager.authorizationStatus)
34
+ handle(manager.authorizationStatus, getter: .instanceCallback)
29
35
  }
30
36
 
31
37
  static func isAuthorized(_ status: CLAuthorizationStatus) -> Bool {
@@ -36,14 +42,16 @@ final class MacHotspotAuthorization: NSObject, CLLocationManagerDelegate {
36
42
  return false
37
43
  }
38
44
 
39
- private func handle(_ status: CLAuthorizationStatus) {
45
+ private func handle(_ status: CLAuthorizationStatus, getter: MacHotspotAuthorizationFacts.Getter) {
46
+ let foreground = MacHotspotAuthorizationFacts.foreground()
47
+ if !finished { facts.observeInstance(raw: status.rawValue, getter: getter, foreground: foreground) }
40
48
  if Self.isAuthorized(status) { finish(.success(())); return }
41
49
  switch status {
42
50
  case .denied, .restricted:
43
- finish(.failure(LocalMediaError("Location access is required to identify the glasses hotspot on Mac")))
51
+ finish(.failure(facts.failure("Location access is required to identify the glasses hotspot on Mac", raw: status.rawValue, getter: getter, foreground: foreground)))
44
52
  case .notDetermined: break
45
53
  default:
46
- finish(.failure(LocalMediaError("Mac hotspot Location authorization is unavailable")))
54
+ finish(.failure(facts.failure("Mac hotspot Location authorization is unavailable", raw: status.rawValue, getter: getter, foreground: foreground)))
47
55
  }
48
56
  }
49
57
 
@@ -0,0 +1,51 @@
1
+ import Foundation
2
+ #if canImport(UIKit)
3
+ import UIKit
4
+ #elseif canImport(AppKit)
5
+ import AppKit
6
+ #endif
7
+
8
+ /// Diagnostic observations only. These facts never authorize a network read or a join.
9
+ final class MacHotspotAuthorizationFacts {
10
+ enum Getter: String { case instanceInitial = "instance-initial", instanceCallback = "instance-callback", staticSSID = "static-ssid" }
11
+ enum Foreground: String { case active, inactive, background, unknown }
12
+ private let lock = NSLock()
13
+ private var instance: (raw: Int32, getter: Getter, foreground: Foreground)?
14
+ private var initialCallbackObserved = false
15
+ private let pid = ProcessInfo.processInfo.processIdentifier
16
+ private let bundleID = Bundle.main.bundleIdentifier.flatMap { value in
17
+ value.range(of: "^[A-Za-z0-9_.-]{1,128}$", options: .regularExpression) == nil ? nil : value
18
+ } ?? "unknown"
19
+
20
+ /// UIKit/AppKit state is sampled only on its owning thread. A worker read stays unknown.
21
+ static func foreground() -> Foreground {
22
+ guard Thread.isMainThread else { return .unknown }
23
+ #if canImport(UIKit)
24
+ switch UIApplication.shared.applicationState {
25
+ case .active: return .active
26
+ case .inactive: return .inactive
27
+ case .background: return .background
28
+ @unknown default: return .unknown
29
+ }
30
+ #elseif canImport(AppKit)
31
+ return NSApplication.shared.isActive ? .active : .inactive
32
+ #else
33
+ return .unknown
34
+ #endif
35
+ }
36
+
37
+ func observeInstance(raw: Int32, getter: Getter, foreground: Foreground) {
38
+ lock.lock(); defer { lock.unlock() }
39
+ instance = (raw, getter, foreground)
40
+ if getter == .instanceCallback { initialCallbackObserved = true }
41
+ }
42
+
43
+ /// Only enums and this process's identity cross the existing bounded error bridge.
44
+ func failure(_ message: String, raw: Int32, getter: Getter, foreground: Foreground) -> LocalMediaError {
45
+ lock.lock(); defer { lock.unlock() }
46
+ let instanceRaw = instance.map { String($0.raw) } ?? "unknown"
47
+ let instanceGetter = instance?.getter.rawValue ?? "unknown"
48
+ let instanceForeground = instance?.foreground.rawValue ?? "unknown"
49
+ return LocalMediaError("\(message) [location raw=\(raw) getter=\(getter.rawValue) foreground=\(foreground.rawValue) instanceRaw=\(instanceRaw) instanceGetter=\(instanceGetter) instanceForeground=\(instanceForeground) initialCallbackObserved=\(initialCallbackObserved) pid=\(pid) bundle=\(bundleID)]")
50
+ }
51
+ }
@@ -239,14 +239,15 @@ public final class GlassesHotspotNetwork {
239
239
  }
240
240
 
241
241
  private func authorizeMacAssociation(ssid: String, passphrase: String, generation gen: Int, deadline: TimeInterval) {
242
- let authorization = MacHotspotAuthorization()
242
+ let facts = MacHotspotAuthorizationFacts()
243
+ let authorization = MacHotspotAuthorization(facts: facts)
243
244
  macAuthorization = authorization
244
245
  authorization.start { result in
245
246
  self.queue.async {
246
247
  guard gen == self.generation, !self.cancelled else { return }
247
248
  self.macAuthorization = nil
248
249
  switch result {
249
- case .success: self.applyMacAssociation(ssid: ssid, passphrase: passphrase, generation: gen, deadline: deadline)
250
+ case .success: self.applyMacAssociation(ssid: ssid, passphrase: passphrase, generation: gen, deadline: deadline, authorizationFacts: facts)
250
251
  case let .failure(error):
251
252
  self.finishJoin(.failure(error)); self.finishLeave()
252
253
  }
@@ -254,8 +255,8 @@ public final class GlassesHotspotNetwork {
254
255
  }
255
256
  }
256
257
 
257
- private func applyMacAssociation(ssid: String, passphrase: String, generation gen: Int, deadline: TimeInterval) {
258
- let association = MacHotspotAssociation(deadline: deadline)
258
+ private func applyMacAssociation(ssid: String, passphrase: String, generation gen: Int, deadline: TimeInterval, authorizationFacts: MacHotspotAuthorizationFacts) {
259
+ let association = MacHotspotAssociation(deadline: deadline, authorizationFacts: authorizationFacts)
259
260
  macAssociation = association
260
261
  applying = true
261
262
  macWorker.async {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mentra/glasses-media",
3
- "version": "3.4.0-dev.971",
3
+ "version": "3.4.0-dev.975",
4
4
  "description": "Shared native glasses media receivers and local network transport",
5
5
  "license": "MIT",
6
6
  "author": "Mentra Community",