@mentra/bluetooth-sdk 3.3.0-dev.421 → 3.3.0-dev.422
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.
- package/android/src/main/java/com/mentra/bluetoothsdk/BluetoothSdkModule.kt +0 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +3 -17
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraBluetoothSdk.kt +0 -11
- package/android/src/main/java/com/mentra/bluetoothsdk/services/Foreground.kt +0 -21
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +31 -213
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/SerializedGattCallback.kt +2 -13
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunkReassembler.java +6 -41
- package/build/BluetoothSdk.types.d.ts +0 -10
- package/build/BluetoothSdk.types.d.ts.map +1 -1
- package/build/BluetoothSdk.types.js.map +1 -1
- package/build/_private/BluetoothSdkModule.d.ts +0 -9
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +0 -2
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/ios/Source/BluetoothSdkDefaults.swift +1 -1
- package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
- package/package.json +2 -2
- package/src/BluetoothSdk.types.ts +0 -10
- package/src/_private/BluetoothSdkModule.ts +0 -13
- package/src/generated/releaseMetadata.ts +5 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleEvidenceLog.kt +0 -454
- package/android/src/test/java/com/mentra/bluetoothsdk/WifiScanEvidenceTest.kt +0 -117
- package/android/src/test/java/com/mentra/bluetoothsdk/debug/BleEvidenceLogTest.kt +0 -282
- package/android/src/test/java/com/mentra/bluetoothsdk/sgcs/MentraLiveEvidenceTest.kt +0 -603
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/MessageChunkReassemblerOriginTest.kt +0 -51
- package/build/_private/diagnosticRender.d.ts +0 -12
- package/build/_private/diagnosticRender.d.ts.map +0 -1
- package/build/_private/diagnosticRender.js +0 -27
- package/build/_private/diagnosticRender.js.map +0 -1
- package/src/_private/diagnosticRender.ts +0 -35
|
@@ -43,7 +43,6 @@ import com.mentra.bluetoothsdk.DeviceStore
|
|
|
43
43
|
import com.mentra.bluetoothsdk.ObservableStore
|
|
44
44
|
import com.mentra.bluetoothsdk.incomingGlassesMessageAckId
|
|
45
45
|
import com.mentra.bluetoothsdk.wifiResponseEnvelopeIsValid
|
|
46
|
-
import com.mentra.bluetoothsdk.debug.BleEvidenceLog
|
|
47
46
|
import com.mentra.bluetoothsdk.debug.BleTraceLogger
|
|
48
47
|
import com.mentra.bluetoothsdk.utils.BlePhotoUploadService
|
|
49
48
|
import com.mentra.bluetoothsdk.utils.ConnTypes
|
|
@@ -598,12 +597,6 @@ class MentraLive : SGCManager() {
|
|
|
598
597
|
private val gattLifecycleHandler = Handler(Looper.getMainLooper())
|
|
599
598
|
// Owned exclusively by the main looper, including callback validation and mutation.
|
|
600
599
|
private var gattEpoch = 0L
|
|
601
|
-
// Provenance of the current accepted connection and wire epoch. Copied into each accepted
|
|
602
|
-
// notification before decoding; never re-read to label an already received message.
|
|
603
|
-
private var evidenceOrigin: BleEvidenceLog.Origin? = null
|
|
604
|
-
// Evidence boundary of the current bleSessionGeneration (seq of its ble_generation record).
|
|
605
|
-
@Volatile private var evidenceWriteGeneration = 0L
|
|
606
|
-
@Volatile private var evidenceWriteGenerationValue = -1L
|
|
607
600
|
private var connectionRequestEpoch = 0L
|
|
608
601
|
@Volatile private var gattTeardownToken: Long? = null
|
|
609
602
|
@Volatile private var gattTeardownTimeoutRunnable: Runnable? = null
|
|
@@ -657,9 +650,7 @@ class MentraLive : SGCManager() {
|
|
|
657
650
|
val commandType: String,
|
|
658
651
|
val requestId: String?,
|
|
659
652
|
val appId: String?,
|
|
660
|
-
val messageId: Long
|
|
661
|
-
// Wi-Fi scan request id; every native write of that command is recorded against it.
|
|
662
|
-
val scanId: String? = null
|
|
653
|
+
val messageId: Long?
|
|
663
654
|
)
|
|
664
655
|
|
|
665
656
|
private data class BleWriteTrace(
|
|
@@ -675,11 +666,7 @@ class MentraLive : SGCManager() {
|
|
|
675
666
|
val packedBytes: Int,
|
|
676
667
|
val wakeup: Boolean,
|
|
677
668
|
val chunked: Boolean,
|
|
678
|
-
val queuedAtMs: Long
|
|
679
|
-
val scanId: String? = null,
|
|
680
|
-
// Provenance captured when the write is queued; later outcomes never re-read state.
|
|
681
|
-
val evidenceConnection: Long = 0L,
|
|
682
|
-
val evidenceWriteGeneration: Long? = null
|
|
669
|
+
val queuedAtMs: Long
|
|
683
670
|
)
|
|
684
671
|
|
|
685
672
|
private data class QueuedBleWrite(
|
|
@@ -1037,7 +1024,6 @@ class MentraLive : SGCManager() {
|
|
|
1037
1024
|
this.type = DeviceTypes.LIVE
|
|
1038
1025
|
this.hasMic = true
|
|
1039
1026
|
this.context = Bridge.getContext()
|
|
1040
|
-
recordWriteGeneration(bleSessionGeneration.get())
|
|
1041
1027
|
|
|
1042
1028
|
// Initialize bluetooth adapter
|
|
1043
1029
|
val bluetoothManager =
|
|
@@ -1193,9 +1179,6 @@ class MentraLive : SGCManager() {
|
|
|
1193
1179
|
if (state == ConnTypes.DISCONNECTED) {
|
|
1194
1180
|
DeviceStore.apply("glasses", "hotspotOtaVersion", 0)
|
|
1195
1181
|
}
|
|
1196
|
-
if (state == ConnTypes.DISCONNECTED) {
|
|
1197
|
-
closeEvidenceConnection("disconnected")
|
|
1198
|
-
}
|
|
1199
1182
|
val isEqual = state == connectionState
|
|
1200
1183
|
if (isEqual) {
|
|
1201
1184
|
if (state == ConnTypes.DISCONNECTED) {
|
|
@@ -1709,7 +1692,6 @@ class MentraLive : SGCManager() {
|
|
|
1709
1692
|
}
|
|
1710
1693
|
val token = gattTeardownBarrier.beginTeardown()
|
|
1711
1694
|
gattTeardownToken = token
|
|
1712
|
-
closeEvidenceConnection("teardown")
|
|
1713
1695
|
// Teardown owns session invalidation, including the timeout/replacement paths that do
|
|
1714
1696
|
// not receive the normal remote-disconnect cleanup callback.
|
|
1715
1697
|
cancelMtuWatchdog()
|
|
@@ -1783,34 +1765,6 @@ class MentraLive : SGCManager() {
|
|
|
1783
1765
|
mtuSetupGate.cancel()
|
|
1784
1766
|
}
|
|
1785
1767
|
|
|
1786
|
-
private fun closeEvidenceConnection(reason: String) {
|
|
1787
|
-
evidenceOrigin?.let { BleEvidenceLog.connectionClosed(it.connection, reason) }
|
|
1788
|
-
evidenceOrigin = null
|
|
1789
|
-
}
|
|
1790
|
-
|
|
1791
|
-
/** RX/TX carry every JSON/K900 message; audio and file characteristics are never evidence. */
|
|
1792
|
-
private fun isCommandCharacteristic(characteristic: BluetoothGattCharacteristic): Boolean =
|
|
1793
|
-
characteristic.uuid == RX_CHAR_UUID || characteristic.uuid == TX_CHAR_UUID
|
|
1794
|
-
|
|
1795
|
-
private fun recordScanSend(trace: BleWriteTrace?, outcome: String) {
|
|
1796
|
-
val scanId = trace?.scanId ?: return
|
|
1797
|
-
BleEvidenceLog.scanSend(
|
|
1798
|
-
scanId,
|
|
1799
|
-
outcome,
|
|
1800
|
-
trace.chunkIndex ?: 0,
|
|
1801
|
-
trace.totalChunks ?: 1,
|
|
1802
|
-
trace.evidenceConnection,
|
|
1803
|
-
trace.evidenceWriteGeneration
|
|
1804
|
-
)
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
/** Records the send-queue generation boundary that queued writes will reference. */
|
|
1808
|
-
private fun recordWriteGeneration(generation: Long) {
|
|
1809
|
-
evidenceWriteGeneration =
|
|
1810
|
-
BleEvidenceLog.bleGeneration(evidenceOrigin?.connection ?: 0L, generation)
|
|
1811
|
-
evidenceWriteGenerationValue = generation
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
1768
|
/**
|
|
1815
1769
|
* Reject callbacks delivered by a GATT object from an earlier connection attempt. Without
|
|
1816
1770
|
* this identity check, a late disconnect can tear down the active connection and clear its
|
|
@@ -2322,15 +2276,6 @@ class MentraLive : SGCManager() {
|
|
|
2322
2276
|
// This lets connectGatt return and install the identity before validation.
|
|
2323
2277
|
gattLifecycleHandler.post { work() }
|
|
2324
2278
|
}, { gatt -> epoch == gattEpoch && isCurrentGattCallback(gatt) }) {
|
|
2325
|
-
override fun onStaleNotification(
|
|
2326
|
-
gatt: BluetoothGatt,
|
|
2327
|
-
characteristic: BluetoothGattCharacteristic
|
|
2328
|
-
) {
|
|
2329
|
-
if (isCommandCharacteristic(characteristic)) {
|
|
2330
|
-
BleEvidenceLog.receiveRejected("stale_gatt", null)
|
|
2331
|
-
}
|
|
2332
|
-
}
|
|
2333
|
-
|
|
2334
2279
|
override fun handleConnectionStateChange(
|
|
2335
2280
|
gatt: BluetoothGatt,
|
|
2336
2281
|
status: Int,
|
|
@@ -2368,8 +2313,6 @@ class MentraLive : SGCManager() {
|
|
|
2368
2313
|
isConnecting = false
|
|
2369
2314
|
isConnected = true
|
|
2370
2315
|
connectedDevice = gatt.device
|
|
2371
|
-
closeEvidenceConnection("replaced")
|
|
2372
|
-
evidenceOrigin = BleEvidenceLog.connectionAccepted(gatt.device.address)
|
|
2373
2316
|
classicAudioConnectionTracker.setTarget(gatt.device.address)
|
|
2374
2317
|
refreshClassicAudioConnectionState(gatt.device)
|
|
2375
2318
|
emitConnectedPendingDeviceForPairingScan()
|
|
@@ -2441,7 +2384,6 @@ class MentraLive : SGCManager() {
|
|
|
2441
2384
|
)
|
|
2442
2385
|
isConnected = false
|
|
2443
2386
|
isConnecting = false
|
|
2444
|
-
closeEvidenceConnection("remote_disconnect")
|
|
2445
2387
|
|
|
2446
2388
|
connectedDevice = null
|
|
2447
2389
|
glassesReady = false // Reset ready state on disconnect
|
|
@@ -2758,10 +2700,6 @@ class MentraLive : SGCManager() {
|
|
|
2758
2700
|
else null
|
|
2759
2701
|
inFlightBleWriteTrace = null
|
|
2760
2702
|
inFlightBleWriteStartedAtMs = 0L
|
|
2761
|
-
recordScanSend(
|
|
2762
|
-
trace,
|
|
2763
|
-
if (status == BluetoothGatt.GATT_SUCCESS) "write_ok" else "write_failed"
|
|
2764
|
-
)
|
|
2765
2703
|
|
|
2766
2704
|
if (status == BluetoothGatt.GATT_SUCCESS) {
|
|
2767
2705
|
// Bridge.log("LIVE: Characteristic write successful");
|
|
@@ -2821,13 +2759,8 @@ class MentraLive : SGCManager() {
|
|
|
2821
2759
|
data: ByteArray
|
|
2822
2760
|
) {
|
|
2823
2761
|
if (!isActiveGattCallback(gatt)) {
|
|
2824
|
-
if (isCommandCharacteristic(characteristic)) {
|
|
2825
|
-
BleEvidenceLog.receiveRejected("teardown", null)
|
|
2826
|
-
}
|
|
2827
2762
|
return
|
|
2828
2763
|
}
|
|
2829
|
-
// Immutable origin of this notification, captured before any decoding.
|
|
2830
|
-
val origin = evidenceOrigin
|
|
2831
2764
|
|
|
2832
2765
|
// Get thread ID for tracking thread issues
|
|
2833
2766
|
val threadId = Thread.currentThread().id
|
|
@@ -2874,7 +2807,7 @@ class MentraLive : SGCManager() {
|
|
|
2874
2807
|
}
|
|
2875
2808
|
|
|
2876
2809
|
// Process command/JSON data on RX/TX characteristics
|
|
2877
|
-
processReceivedData(data, data.size
|
|
2810
|
+
processReceivedData(data, data.size)
|
|
2878
2811
|
}
|
|
2879
2812
|
|
|
2880
2813
|
override fun handleDescriptorWrite(
|
|
@@ -3290,7 +3223,6 @@ class MentraLive : SGCManager() {
|
|
|
3290
3223
|
var queuedWrite = sendQueue.poll()
|
|
3291
3224
|
val currentGeneration = bleSessionGeneration.get()
|
|
3292
3225
|
while (queuedWrite != null && queuedWrite.sessionGeneration != currentGeneration) {
|
|
3293
|
-
recordScanSend(queuedWrite.trace, "stale_dropped")
|
|
3294
3226
|
Bridge.log(
|
|
3295
3227
|
"LIVE: Dropping stale queued write from BLE session " +
|
|
3296
3228
|
queuedWrite.sessionGeneration +
|
|
@@ -3326,15 +3258,10 @@ class MentraLive : SGCManager() {
|
|
|
3326
3258
|
|
|
3327
3259
|
/** Send data through BLE */
|
|
3328
3260
|
private fun sendDataInternal(write: QueuedBleWrite?) {
|
|
3329
|
-
if (write == null) {
|
|
3330
|
-
return
|
|
3331
|
-
}
|
|
3332
|
-
if (!isConnected) {
|
|
3333
|
-
recordScanSend(write.trace, "gatt_refused")
|
|
3261
|
+
if (!isConnected || write == null) {
|
|
3334
3262
|
return
|
|
3335
3263
|
}
|
|
3336
3264
|
if (write.sessionGeneration != bleSessionGeneration.get()) {
|
|
3337
|
-
recordScanSend(write.trace, "stale_dropped")
|
|
3338
3265
|
Bridge.log("LIVE: Dropping stale BLE write before GATT transmission")
|
|
3339
3266
|
return
|
|
3340
3267
|
}
|
|
@@ -3342,7 +3269,6 @@ class MentraLive : SGCManager() {
|
|
|
3342
3269
|
val gatt = bluetoothGatt
|
|
3343
3270
|
val characteristic = txCharacteristic
|
|
3344
3271
|
if (gatt == null || characteristic == null) {
|
|
3345
|
-
recordScanSend(write.trace, "gatt_refused")
|
|
3346
3272
|
return
|
|
3347
3273
|
}
|
|
3348
3274
|
|
|
@@ -3352,7 +3278,6 @@ class MentraLive : SGCManager() {
|
|
|
3352
3278
|
inFlightBleWriteTrace = write.trace
|
|
3353
3279
|
inFlightBleWriteStartedAtMs = writeStartedAtMs
|
|
3354
3280
|
val writeAccepted = gatt.writeCharacteristic(characteristic)
|
|
3355
|
-
recordScanSend(write.trace, if (writeAccepted) "gatt_accepted" else "gatt_refused")
|
|
3356
3281
|
logBleWriteTrace(
|
|
3357
3282
|
"write_call",
|
|
3358
3283
|
write.trace,
|
|
@@ -3369,7 +3294,6 @@ class MentraLive : SGCManager() {
|
|
|
3369
3294
|
inFlightBleWriteStartedAtMs = 0L
|
|
3370
3295
|
}
|
|
3371
3296
|
} catch (e: Exception) {
|
|
3372
|
-
recordScanSend(write.trace, "gatt_refused")
|
|
3373
3297
|
Log.e(TAG, "Error sending data via BLE", e)
|
|
3374
3298
|
logBleWriteTrace(
|
|
3375
3299
|
"write_exception",
|
|
@@ -3396,16 +3320,9 @@ class MentraLive : SGCManager() {
|
|
|
3396
3320
|
queuedAtMs =
|
|
3397
3321
|
if (trace.queuedAtMs > 0L)
|
|
3398
3322
|
trace.queuedAtMs
|
|
3399
|
-
else System.currentTimeMillis()
|
|
3400
|
-
evidenceConnection = evidenceOrigin?.connection ?: 0L,
|
|
3401
|
-
// A write carrying an older generation is already stale: no boundary.
|
|
3402
|
-
evidenceWriteGeneration =
|
|
3403
|
-
if (sessionGeneration == evidenceWriteGenerationValue)
|
|
3404
|
-
evidenceWriteGeneration
|
|
3405
|
-
else null
|
|
3323
|
+
else System.currentTimeMillis()
|
|
3406
3324
|
)
|
|
3407
3325
|
sendQueue.add(QueuedBleWrite(data, queuedTrace, sessionGeneration))
|
|
3408
|
-
recordScanSend(queuedTrace, "queued")
|
|
3409
3326
|
logBleChunkTrace(
|
|
3410
3327
|
"queued",
|
|
3411
3328
|
queuedTrace,
|
|
@@ -3626,7 +3543,6 @@ class MentraLive : SGCManager() {
|
|
|
3626
3543
|
*/
|
|
3627
3544
|
private fun resetPendingAckState() {
|
|
3628
3545
|
val nextGeneration = bleSessionGeneration.incrementAndGet()
|
|
3629
|
-
recordWriteGeneration(nextGeneration)
|
|
3630
3546
|
val pendingCount = pendingMessages.size
|
|
3631
3547
|
pendingMessages.clear()
|
|
3632
3548
|
if (pendingCount > 0) {
|
|
@@ -3955,7 +3871,7 @@ class MentraLive : SGCManager() {
|
|
|
3955
3871
|
}
|
|
3956
3872
|
|
|
3957
3873
|
/** Process data received from the glasses */
|
|
3958
|
-
private fun processReceivedData(data: ByteArray?, size: Int
|
|
3874
|
+
private fun processReceivedData(data: ByteArray?, size: Int) {
|
|
3959
3875
|
// Bridge.log("LIVE: Processing received data: " + bytesToHex(data));
|
|
3960
3876
|
|
|
3961
3877
|
// Check if we have enough data
|
|
@@ -4022,7 +3938,7 @@ class MentraLive : SGCManager() {
|
|
|
4022
3938
|
}
|
|
4023
3939
|
|
|
4024
3940
|
if (cmdType == K900ProtocolUtils.CMD_TYPE_BINARY_MSG) {
|
|
4025
|
-
processBinaryWireFrame(data
|
|
3941
|
+
processBinaryWireFrame(data)
|
|
4026
3942
|
return
|
|
4027
3943
|
}
|
|
4028
3944
|
|
|
@@ -4040,7 +3956,7 @@ class MentraLive : SGCManager() {
|
|
|
4040
3956
|
Log.w(TAG, "Thread-$threadId: Rejected unsupported compact wire form")
|
|
4041
3957
|
return
|
|
4042
3958
|
}
|
|
4043
|
-
processJsonMessage(expanded
|
|
3959
|
+
processJsonMessage(expanded)
|
|
4044
3960
|
} else {
|
|
4045
3961
|
Log.w(TAG, "Thread-" + threadId + ": Failed to parse K900 protocol data")
|
|
4046
3962
|
}
|
|
@@ -4065,7 +3981,7 @@ class MentraLive : SGCManager() {
|
|
|
4065
3981
|
val jsonStr = String(data, 0, size, StandardCharsets.UTF_8)
|
|
4066
3982
|
if (jsonStr.startsWith("{") && jsonStr.endsWith("}")) {
|
|
4067
3983
|
val json = JSONObject(jsonStr)
|
|
4068
|
-
processJsonMessage(json
|
|
3984
|
+
processJsonMessage(json)
|
|
4069
3985
|
} else {
|
|
4070
3986
|
Log.w(TAG, "Received data that starts with '{' but is not valid JSON")
|
|
4071
3987
|
}
|
|
@@ -4088,7 +4004,7 @@ class MentraLive : SGCManager() {
|
|
|
4088
4004
|
}
|
|
4089
4005
|
|
|
4090
4006
|
/** Process a JSON message */
|
|
4091
|
-
private fun processJsonMessage(json: JSONObject
|
|
4007
|
+
private fun processJsonMessage(json: JSONObject) {
|
|
4092
4008
|
// Demoted from INFO (Bridge.log) to DEBUG: per-type handlers below already log
|
|
4093
4009
|
// the messages that matter, and full payloads can leak PII (wifi SSID, bt_mac,
|
|
4094
4010
|
// OTA URLs) into the persisted file logger when they arrive every ~50ms during OTA.
|
|
@@ -4100,7 +4016,7 @@ class MentraLive : SGCManager() {
|
|
|
4100
4016
|
GlassesLinkDiagnostics.recordInbound()
|
|
4101
4017
|
|
|
4102
4018
|
if (MessageChunker.isChunkedMessage(json)) {
|
|
4103
|
-
processChunkedJsonMessage(json
|
|
4019
|
+
processChunkedJsonMessage(json)
|
|
4104
4020
|
return
|
|
4105
4021
|
}
|
|
4106
4022
|
|
|
@@ -4119,7 +4035,7 @@ class MentraLive : SGCManager() {
|
|
|
4119
4035
|
|
|
4120
4036
|
// Check if this is a K900 command format (has "C" field instead of "type")
|
|
4121
4037
|
if (json.has("C")) {
|
|
4122
|
-
processK900JsonMessage(json
|
|
4038
|
+
processK900JsonMessage(json)
|
|
4123
4039
|
return
|
|
4124
4040
|
}
|
|
4125
4041
|
|
|
@@ -4215,15 +4131,7 @@ class MentraLive : SGCManager() {
|
|
|
4215
4131
|
// that reads "charging" for most of a discharging pack's range. Charging
|
|
4216
4132
|
// state comes exclusively from the PMU charg bit in the sr_hrt heartbeat.
|
|
4217
4133
|
val percent = json.optInt("percent", batteryLevel)
|
|
4218
|
-
|
|
4219
|
-
// measured percentage carries provenance.
|
|
4220
|
-
updateBatteryStatus(
|
|
4221
|
-
percent,
|
|
4222
|
-
isCharging,
|
|
4223
|
-
measuredPercent(json, "percent")?.let {
|
|
4224
|
-
BatteryProvenance("battery_status", origin, null)
|
|
4225
|
-
}
|
|
4226
|
-
)
|
|
4134
|
+
updateBatteryStatus(percent, isCharging)
|
|
4227
4135
|
}
|
|
4228
4136
|
"stream_controller_probe" -> {
|
|
4229
4137
|
val values = mapOf("protocolVersion" to json.opt("protocolVersion"),
|
|
@@ -4398,8 +4306,6 @@ class MentraLive : SGCManager() {
|
|
|
4398
4306
|
"wifi_scan_result" -> {
|
|
4399
4307
|
// Process WiFi scan results
|
|
4400
4308
|
val networks: MutableList<Map<String, Any>> = ArrayList()
|
|
4401
|
-
// Evidence only: false when the network list is missing or not fully parsed.
|
|
4402
|
-
var networksParsed = false
|
|
4403
4309
|
|
|
4404
4310
|
if (json.has("networks_neo")) {
|
|
4405
4311
|
try {
|
|
@@ -4417,7 +4323,6 @@ class MentraLive : SGCManager() {
|
|
|
4417
4323
|
}
|
|
4418
4324
|
networks.add(networkMap)
|
|
4419
4325
|
}
|
|
4420
|
-
networksParsed = true
|
|
4421
4326
|
|
|
4422
4327
|
Bridge.log(
|
|
4423
4328
|
"Received enhanced WiFi scan results: " +
|
|
@@ -4432,9 +4337,7 @@ class MentraLive : SGCManager() {
|
|
|
4432
4337
|
val scanComplete =
|
|
4433
4338
|
json.optBoolean("scan_complete", json.optBoolean("scanComplete", false))
|
|
4434
4339
|
val scanId = json.optString("scanId", "").ifEmpty { null }
|
|
4435
|
-
|
|
4436
|
-
BleEvidenceLog.scanChunk(origin, scanId, networks, scanComplete, networksParsed)
|
|
4437
|
-
Bridge.updateWifiScanResults(networks, scanComplete, scanId, eventId)
|
|
4340
|
+
Bridge.updateWifiScanResults(networks, scanComplete, scanId)
|
|
4438
4341
|
}
|
|
4439
4342
|
"token_status" -> {
|
|
4440
4343
|
// Process coreToken acknowledgment
|
|
@@ -5308,7 +5211,7 @@ class MentraLive : SGCManager() {
|
|
|
5308
5211
|
return value
|
|
5309
5212
|
}
|
|
5310
5213
|
|
|
5311
|
-
private fun processChunkedJsonMessage(json: JSONObject
|
|
5214
|
+
private fun processChunkedJsonMessage(json: JSONObject) {
|
|
5312
5215
|
try {
|
|
5313
5216
|
val chunkInfo = MessageChunker.getChunkInfo(json)
|
|
5314
5217
|
if (chunkInfo == null) {
|
|
@@ -5326,24 +5229,19 @@ class MentraLive : SGCManager() {
|
|
|
5326
5229
|
return
|
|
5327
5230
|
}
|
|
5328
5231
|
|
|
5329
|
-
val mixedBefore = incomingChunkReassembler.mixedOriginDrops
|
|
5330
5232
|
val reassembled =
|
|
5331
5233
|
incomingChunkReassembler.addChunk(
|
|
5332
5234
|
chunkInfo.chunkId,
|
|
5333
5235
|
chunkInfo.chunkIndex,
|
|
5334
5236
|
chunkInfo.totalChunks,
|
|
5335
|
-
chunkInfo.data
|
|
5336
|
-
origin
|
|
5237
|
+
chunkInfo.data
|
|
5337
5238
|
)
|
|
5338
|
-
if (incomingChunkReassembler.mixedOriginDrops != mixedBefore) {
|
|
5339
|
-
BleEvidenceLog.receiveRejected("mixed_origin", origin)
|
|
5340
|
-
}
|
|
5341
5239
|
if (reassembled == null) {
|
|
5342
5240
|
return
|
|
5343
5241
|
}
|
|
5344
5242
|
|
|
5345
5243
|
val reassembledJson = JSONObject(reassembled)
|
|
5346
|
-
processJsonMessage(reassembledJson
|
|
5244
|
+
processJsonMessage(reassembledJson)
|
|
5347
5245
|
} catch (e: Exception) {
|
|
5348
5246
|
Log.e(TAG, "Error processing chunked JSON message", e)
|
|
5349
5247
|
}
|
|
@@ -5551,7 +5449,7 @@ class MentraLive : SGCManager() {
|
|
|
5551
5449
|
}
|
|
5552
5450
|
}
|
|
5553
5451
|
|
|
5554
|
-
private fun processK900JsonMessage(json: JSONObject
|
|
5452
|
+
private fun processK900JsonMessage(json: JSONObject) {
|
|
5555
5453
|
val command = json.optString("C", "")
|
|
5556
5454
|
// Bridge.log("LIVE: Processing K900 command: " + command);
|
|
5557
5455
|
|
|
@@ -5595,17 +5493,7 @@ class MentraLive : SGCManager() {
|
|
|
5595
5493
|
}
|
|
5596
5494
|
val charg = bodyObj.optInt("charg", -1)
|
|
5597
5495
|
if (batteryPercentage != -1 && charg != -1)
|
|
5598
|
-
updateBatteryStatus(
|
|
5599
|
-
batteryPercentage,
|
|
5600
|
-
charg == 1,
|
|
5601
|
-
measuredPercent(bodyObj, "pt")?.let {
|
|
5602
|
-
BatteryProvenance(
|
|
5603
|
-
"k900_sr_hrt",
|
|
5604
|
-
origin,
|
|
5605
|
-
pmuChargingBit(bodyObj)
|
|
5606
|
-
)
|
|
5607
|
-
}
|
|
5608
|
-
)
|
|
5496
|
+
updateBatteryStatus(batteryPercentage, charg == 1)
|
|
5609
5497
|
}
|
|
5610
5498
|
} catch (e: Exception) {
|
|
5611
5499
|
Log.e(TAG, "Error parsing sr_hrt response", e)
|
|
@@ -5636,13 +5524,7 @@ class MentraLive : SGCManager() {
|
|
|
5636
5524
|
// charging from voltage (>4.0V) reads "not charging" for most of a
|
|
5637
5525
|
// genuinely-charging pack's range. Charging state comes exclusively
|
|
5638
5526
|
// from the PMU charg bit in the sr_hrt heartbeat.
|
|
5639
|
-
updateBatteryStatus(
|
|
5640
|
-
batteryPercentage,
|
|
5641
|
-
isCharging,
|
|
5642
|
-
measuredPercent(bodyObj, "pt")?.let {
|
|
5643
|
-
BatteryProvenance("k900_sr_batv", origin, null)
|
|
5644
|
-
}
|
|
5645
|
-
)
|
|
5527
|
+
updateBatteryStatus(batteryPercentage, isCharging)
|
|
5646
5528
|
}
|
|
5647
5529
|
} catch (e: Exception) {
|
|
5648
5530
|
Log.e(TAG, "Error parsing sr_batv response", e)
|
|
@@ -5879,7 +5761,7 @@ class MentraLive : SGCManager() {
|
|
|
5879
5761
|
Log.d(TAG, "🔓 Unwrapping and processing through standard message handler")
|
|
5880
5762
|
|
|
5881
5763
|
// Process through the standard message handler
|
|
5882
|
-
processJsonMessage(innerJson
|
|
5764
|
+
processJsonMessage(innerJson)
|
|
5883
5765
|
return // Exit after processing
|
|
5884
5766
|
}
|
|
5885
5767
|
} catch (e: JSONException) {
|
|
@@ -5993,44 +5875,7 @@ class MentraLive : SGCManager() {
|
|
|
5993
5875
|
* Update battery status and notify listeners Matches iOS MentraLive.swift updateBatteryStatus
|
|
5994
5876
|
* pattern
|
|
5995
5877
|
*/
|
|
5996
|
-
|
|
5997
|
-
* [source] names the decoded message. [pmuCharging] is set only when the PMU charg bit of
|
|
5998
|
-
* this very message supplied [isCharging]; percent-only sources re-pass the inherited value.
|
|
5999
|
-
*/
|
|
6000
|
-
/**
|
|
6001
|
-
* Provenance of a battery update decoded from one notification. [pmuCharging] is set only
|
|
6002
|
-
* when the PMU charg bit of that message supplied the charging state.
|
|
6003
|
-
*/
|
|
6004
|
-
private data class BatteryProvenance(
|
|
6005
|
-
val source: String,
|
|
6006
|
-
val origin: BleEvidenceLog.Origin?,
|
|
6007
|
-
val pmuCharging: Boolean?
|
|
6008
|
-
)
|
|
6009
|
-
|
|
6010
|
-
/**
|
|
6011
|
-
* The sr_hrt PMU charging bit, only when `charg` is a number exactly equal to 0 or 1. The
|
|
6012
|
-
* original value is compared without truncation, so 0.5 or 1.5 is not a bit.
|
|
6013
|
-
*/
|
|
6014
|
-
private fun pmuChargingBit(body: JSONObject): Boolean? =
|
|
6015
|
-
when ((body.opt("charg") as? Number)?.toDouble()) {
|
|
6016
|
-
1.0 -> true
|
|
6017
|
-
0.0 -> false
|
|
6018
|
-
else -> null
|
|
6019
|
-
}
|
|
6020
|
-
|
|
6021
|
-
/** The field's value when it is an actual integer percentage, else null (missing/invalid). */
|
|
6022
|
-
private fun measuredPercent(obj: JSONObject, key: String): Int? {
|
|
6023
|
-
val value = obj.opt(key) as? Number ?: return null
|
|
6024
|
-
val asDouble = value.toDouble()
|
|
6025
|
-
if (asDouble != Math.floor(asDouble) || asDouble < 0 || asDouble > 100) return null
|
|
6026
|
-
return asDouble.toInt()
|
|
6027
|
-
}
|
|
6028
|
-
|
|
6029
|
-
/**
|
|
6030
|
-
* [provenance] is null for fallback or cached values: they are still displayed as before but
|
|
6031
|
-
* carry no event id, so they can never count as a fresh measurement.
|
|
6032
|
-
*/
|
|
6033
|
-
private fun updateBatteryStatus(level: Int, isCharging: Boolean, provenance: BatteryProvenance?) {
|
|
5878
|
+
private fun updateBatteryStatus(level: Int, isCharging: Boolean) {
|
|
6034
5879
|
// Keep the field in sync: percent-only messages (battery_status/sr_batv) re-pass
|
|
6035
5880
|
// it as the last-known charging state, so a stale field would clobber the value
|
|
6036
5881
|
// the sr_hrt PMU charg bit established.
|
|
@@ -6041,11 +5886,7 @@ class MentraLive : SGCManager() {
|
|
|
6041
5886
|
DeviceStore.apply("glasses", "charging", isCharging)
|
|
6042
5887
|
|
|
6043
5888
|
if (level >= 0) {
|
|
6044
|
-
|
|
6045
|
-
provenance?.let {
|
|
6046
|
-
BleEvidenceLog.battery(it.origin, level, it.source, it.pmuCharging)
|
|
6047
|
-
}
|
|
6048
|
-
Bridge.sendBatteryStatus(level, isCharging, eventId)
|
|
5889
|
+
Bridge.sendBatteryStatus(level, isCharging)
|
|
6049
5890
|
}
|
|
6050
5891
|
}
|
|
6051
5892
|
|
|
@@ -6222,17 +6063,7 @@ class MentraLive : SGCManager() {
|
|
|
6222
6063
|
if (!scanId.isNullOrEmpty()) {
|
|
6223
6064
|
json.put("scanId", scanId)
|
|
6224
6065
|
}
|
|
6225
|
-
|
|
6226
|
-
if (!queued && !scanId.isNullOrEmpty()) {
|
|
6227
|
-
BleEvidenceLog.scanSend(
|
|
6228
|
-
scanId,
|
|
6229
|
-
"not_queued",
|
|
6230
|
-
0,
|
|
6231
|
-
0,
|
|
6232
|
-
evidenceOrigin?.connection ?: 0L,
|
|
6233
|
-
evidenceWriteGeneration
|
|
6234
|
-
)
|
|
6235
|
-
}
|
|
6066
|
+
sendJson(json, true)
|
|
6236
6067
|
Bridge.log("LIVE: Sending WiFi scan request to glasses")
|
|
6237
6068
|
} catch (e: JSONException) {
|
|
6238
6069
|
Log.e(TAG, "Error creating WiFi scan request", e)
|
|
@@ -9178,9 +9009,6 @@ class MentraLive : SGCManager() {
|
|
|
9178
9009
|
* the per-session handshake attempt cap) instead of trusting stale v2-active state.
|
|
9179
9010
|
*/
|
|
9180
9011
|
private fun resetWireNegotiationState() {
|
|
9181
|
-
evidenceOrigin?.let {
|
|
9182
|
-
evidenceOrigin = it.copy(wire = BleEvidenceLog.wireReset(it.connection))
|
|
9183
|
-
}
|
|
9184
9012
|
incomingChunkReassembler.clear()
|
|
9185
9013
|
peerWireProtocolVersion = 0
|
|
9186
9014
|
useBinaryWireProtocol = false
|
|
@@ -9416,7 +9244,7 @@ class MentraLive : SGCManager() {
|
|
|
9416
9244
|
activateBinaryWireV2Session("LIVE: Peer confirmed BLE wire protocol v2")
|
|
9417
9245
|
}
|
|
9418
9246
|
|
|
9419
|
-
private fun processBinaryWireFrame(data: ByteArray
|
|
9247
|
+
private fun processBinaryWireFrame(data: ByteArray) {
|
|
9420
9248
|
val info = K900ProtocolUtils.extractBinaryFragmentInfo(data) ?: run {
|
|
9421
9249
|
Log.w(TAG, "Failed to parse binary wire frame")
|
|
9422
9250
|
return
|
|
@@ -9433,19 +9261,14 @@ class MentraLive : SGCManager() {
|
|
|
9433
9261
|
)
|
|
9434
9262
|
}
|
|
9435
9263
|
|
|
9436
|
-
val mixedBefore = incomingChunkReassembler.mixedOriginDrops
|
|
9437
9264
|
val reassembled =
|
|
9438
9265
|
incomingChunkReassembler.addBinaryFragment(
|
|
9439
9266
|
info.msgId,
|
|
9440
9267
|
info.fragIdx,
|
|
9441
9268
|
info.fragCount,
|
|
9442
|
-
info.payload
|
|
9443
|
-
origin
|
|
9269
|
+
info.payload
|
|
9444
9270
|
)
|
|
9445
|
-
|
|
9446
|
-
BleEvidenceLog.receiveRejected("mixed_origin", origin)
|
|
9447
|
-
}
|
|
9448
|
-
if (reassembled == null) return
|
|
9271
|
+
?: return
|
|
9449
9272
|
|
|
9450
9273
|
try {
|
|
9451
9274
|
val jsonStr = String(reassembled, StandardCharsets.UTF_8)
|
|
@@ -9461,7 +9284,7 @@ class MentraLive : SGCManager() {
|
|
|
9461
9284
|
BleWireProtocol.PROTOCOL_V2,
|
|
9462
9285
|
"glasses_to_phone"
|
|
9463
9286
|
)
|
|
9464
|
-
processJsonMessage(json
|
|
9287
|
+
processJsonMessage(json)
|
|
9465
9288
|
} catch (e: JSONException) {
|
|
9466
9289
|
Log.e(TAG, "Failed to parse reassembled binary wire JSON", e)
|
|
9467
9290
|
}
|
|
@@ -9806,15 +9629,11 @@ class MentraLive : SGCManager() {
|
|
|
9806
9629
|
private fun parseOutgoingBleCommandTraceInfo(payload: String): OutgoingBleCommandTraceInfo {
|
|
9807
9630
|
return try {
|
|
9808
9631
|
val obj = JSONObject(payload)
|
|
9809
|
-
val commandType = obj.optString("type", "unknown")
|
|
9810
9632
|
OutgoingBleCommandTraceInfo(
|
|
9811
|
-
commandType =
|
|
9633
|
+
commandType = obj.optString("type", "unknown"),
|
|
9812
9634
|
requestId = optNonBlankString(obj, "requestId"),
|
|
9813
9635
|
appId = optNonBlankString(obj, "appId"),
|
|
9814
|
-
messageId = if (obj.has("mId")) obj.optLong("mId") else null
|
|
9815
|
-
scanId =
|
|
9816
|
-
if (commandType == "request_wifi_scan") optNonBlankString(obj, "scanId")
|
|
9817
|
-
else null
|
|
9636
|
+
messageId = if (obj.has("mId")) obj.optLong("mId") else null
|
|
9818
9637
|
)
|
|
9819
9638
|
} catch (_: Exception) {
|
|
9820
9639
|
OutgoingBleCommandTraceInfo(
|
|
@@ -9849,8 +9668,7 @@ class MentraLive : SGCManager() {
|
|
|
9849
9668
|
packedBytes = packedBytes,
|
|
9850
9669
|
wakeup = wakeup,
|
|
9851
9670
|
chunked = chunked,
|
|
9852
|
-
queuedAtMs = System.currentTimeMillis()
|
|
9853
|
-
scanId = commandInfo.scanId
|
|
9671
|
+
queuedAtMs = System.currentTimeMillis()
|
|
9854
9672
|
)
|
|
9855
9673
|
}
|
|
9856
9674
|
|
|
@@ -18,17 +18,6 @@ internal abstract class SerializedGattCallback(
|
|
|
18
18
|
enqueue { if (isCurrent(gatt)) work() }
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
private fun dispatchNotification(
|
|
22
|
-
gatt: BluetoothGatt,
|
|
23
|
-
characteristic: BluetoothGattCharacteristic,
|
|
24
|
-
work: () -> Unit,
|
|
25
|
-
) {
|
|
26
|
-
enqueue { if (isCurrent(gatt)) work() else onStaleNotification(gatt, characteristic) }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** A notification from a GATT that is no longer current was dropped on the owner queue. */
|
|
30
|
-
protected open fun onStaleNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {}
|
|
31
|
-
|
|
32
21
|
final override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) =
|
|
33
22
|
dispatch(gatt) { handleConnectionStateChange(gatt, status, newState) }
|
|
34
23
|
|
|
@@ -57,14 +46,14 @@ internal abstract class SerializedGattCallback(
|
|
|
57
46
|
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic,
|
|
58
47
|
) {
|
|
59
48
|
val value = characteristic.value?.copyOf() ?: return
|
|
60
|
-
|
|
49
|
+
dispatch(gatt) { handleCharacteristicChanged(gatt, characteristic, value) }
|
|
61
50
|
}
|
|
62
51
|
|
|
63
52
|
final override fun onCharacteristicChanged(
|
|
64
53
|
gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, value: ByteArray,
|
|
65
54
|
) {
|
|
66
55
|
val snapshot = value.copyOf()
|
|
67
|
-
|
|
56
|
+
dispatch(gatt) { handleCharacteristicChanged(gatt, characteristic, snapshot) }
|
|
68
57
|
}
|
|
69
58
|
|
|
70
59
|
final override fun onDescriptorWrite(
|