@mentra/bluetooth-sdk 3.2.0-dev.191 → 3.2.0-dev.193
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/README.md +18 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +1 -3
- package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
- package/android/src/main/java/com/mentra/bluetoothsdk/MentraLocalNetworkModule.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/otaserver/MentraOtaServerModule.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/MentraPhotoReceiverModule.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/services/PhoneMic.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Ar99.java +8 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/AES.java +7 -8
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BitmapJavaUtils.java +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +34 -34
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/G1Text.kt +1 -1
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleUploadService.java +4 -4
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/K900ProtocolUtils.java +34 -34
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunkReassembler.java +10 -10
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +6 -6
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/NativeLog.java +89 -0
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +2 -2
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +10 -10
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +7 -7
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Lc3Player.java +16 -16
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PCMAudioPlayer.java +26 -26
- package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +1 -1
- package/android/src/test/java/com/mentra/bluetoothsdk/utils/NativeLogTest.kt +61 -0
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
- package/build/_private/BluetoothSdkModule.js +3 -0
- package/build/_private/BluetoothSdkModule.js.map +1 -1
- package/build/_private/nativeLogConsole.d.ts +17 -0
- package/build/_private/nativeLogConsole.d.ts.map +1 -0
- package/build/_private/nativeLogConsole.js +16 -0
- package/build/_private/nativeLogConsole.js.map +1 -0
- 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/Bridge.swift +3 -2
- package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
- package/ios/Source/internal/BleTraceLogger.swift +3 -0
- package/ios/Source/sgcs/G1.swift +1 -1
- package/ios/Source/sgcs/MentraLive.swift +8 -8
- package/ios/Source/stt/STTTools.swift +1 -1
- package/ios/Source/utils/G1Text.swift +1 -1
- package/ios/Source/utils/JSCExperiment.swift +1 -2
- package/ios/Source/utils/MessageChunkReassembler.swift +6 -6
- package/ios/Source/utils/MessageChunker.swift +8 -8
- package/ios/Source/utils/Models.swift +1 -1
- package/ios/Tests/NativeLogTests.swift +34 -0
- package/package.json +1 -1
- package/src/_private/BluetoothSdkModule.ts +4 -0
- package/src/_private/nativeLogConsole.ts +24 -0
- package/src/generated/releaseMetadata.ts +5 -5
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// The native module survives Fast Refresh. Replace its previous subscription
|
|
2
|
+
// when JS reloads this module, rather than printing each event more than once.
|
|
3
|
+
const subscriptionKey = Symbol.for("mentra.bluetooth-sdk.native-log-console");
|
|
4
|
+
const credentialPattern = /(?<![A-Za-z0-9])(?:access[_-]?token|refresh[_-]?token|auth[_-]?token|id[_-]?token|client[_-]?secret|api[_-]?key)(?![A-Za-z0-9])|(?<![A-Za-z0-9])(?:token|password|secret|authorization|auth|key)(?![A-Za-z0-9])\s*["']?\s*(?:is\s*)?[:=]\s*["']?\S+|(?<![A-Za-z0-9])bearer(?![A-Za-z0-9])\s+\S+/i;
|
|
5
|
+
export function nativeLogMessage(message) {
|
|
6
|
+
return credentialPattern.test(message) ? "[REDACTED]" : message.slice(0, 16_384);
|
|
7
|
+
}
|
|
8
|
+
export function installNativeLogConsole(source, platform) {
|
|
9
|
+
source[subscriptionKey]?.remove();
|
|
10
|
+
source[subscriptionKey] = source.addListener("log", ({ message }) => {
|
|
11
|
+
// Resolve console at delivery time so host console interception receives
|
|
12
|
+
// native diagnostics in the same stream as ordinary JS logs.
|
|
13
|
+
console.log(`[native:${platform}]`, nativeLogMessage(message));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=nativeLogConsole.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nativeLogConsole.js","sourceRoot":"","sources":["../../src/_private/nativeLogConsole.ts"],"names":[],"mappings":"AAIA,6EAA6E;AAC7E,+EAA+E;AAC/E,MAAM,eAAe,GAAG,MAAM,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAA;AAG7E,MAAM,iBAAiB,GACrB,kSAAkS,CAAA;AAEpS,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,OAAO,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAA;AAClF,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,MAA2B,EAAE,QAAgB;IACnF,MAAM,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IACjC,MAAM,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,EAAC,OAAO,EAAC,EAAE,EAAE;QAChE,yEAAyE;QACzE,6DAA6D;QAC7D,OAAO,CAAC,GAAG,CAAC,WAAW,QAAQ,GAAG,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAA;IAChE,CAAC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["type LogSource = {\n addListener(event: \"log\", listener: (event: {message: string}) => void): {remove(): void}\n}\n\n// The native module survives Fast Refresh. Replace its previous subscription\n// when JS reloads this module, rather than printing each event more than once.\nconst subscriptionKey = Symbol.for(\"mentra.bluetooth-sdk.native-log-console\")\ntype SubscribedLogSource = LogSource & {[subscriptionKey]?: {remove(): void}}\n\nconst credentialPattern =\n /(?<![A-Za-z0-9])(?:access[_-]?token|refresh[_-]?token|auth[_-]?token|id[_-]?token|client[_-]?secret|api[_-]?key)(?![A-Za-z0-9])|(?<![A-Za-z0-9])(?:token|password|secret|authorization|auth|key)(?![A-Za-z0-9])\\s*[\"']?\\s*(?:is\\s*)?[:=]\\s*[\"']?\\S+|(?<![A-Za-z0-9])bearer(?![A-Za-z0-9])\\s+\\S+/i\n\nexport function nativeLogMessage(message: string): string {\n return credentialPattern.test(message) ? \"[REDACTED]\" : message.slice(0, 16_384)\n}\n\nexport function installNativeLogConsole(source: SubscribedLogSource, platform: string): void {\n source[subscriptionKey]?.remove()\n source[subscriptionKey] = source.addListener(\"log\", ({message}) => {\n // Resolve console at delivery time so host console interception receives\n // native diagnostics in the same stream as ordinary JS logs.\n console.log(`[native:${platform}]`, nativeLogMessage(message))\n })\n}\n"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export const BLUETOOTH_SDK_RELEASE_METADATA = Object.freeze({
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"familyBaseVersion": "3.2.0",
|
|
4
|
-
"releaseIdentity": "3.2.0-dev.
|
|
5
|
-
"releaseSetId": "mentra-3.2.0-dev.
|
|
6
|
-
"sourceCommit": "
|
|
7
|
-
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.
|
|
8
|
-
"otaManifestSha256": "
|
|
4
|
+
"releaseIdentity": "3.2.0-dev.193",
|
|
5
|
+
"releaseSetId": "mentra-3.2.0-dev.193",
|
|
6
|
+
"sourceCommit": "ca3b4bd4109d80253a3369cbf1ca8f9e86236fba",
|
|
7
|
+
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.193.json",
|
|
8
|
+
"otaManifestSha256": "b5201e1a9d926df220adfbf26cba934948d3f09ed81453c3600770a7618dd997"
|
|
9
9
|
});
|
|
10
10
|
//# sourceMappingURL=releaseMetadata.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,8BAA8B,GAA0C,MAAM,CAAC,MAAM,CAAC;IACjG,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface BluetoothSdkReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.
|
|
1
|
+
{"version":3,"file":"releaseMetadata.js","sourceRoot":"","sources":["../../src/generated/releaseMetadata.ts"],"names":[],"mappings":"AAWA,MAAM,CAAC,MAAM,8BAA8B,GAA0C,MAAM,CAAC,MAAM,CAAC;IACjG,eAAe,EAAE,CAAC;IAClB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,eAAe;IAClC,cAAc,EAAE,sBAAsB;IACtC,cAAc,EAAE,0CAA0C;IAC1D,gBAAgB,EAAE,wHAAwH;IAC1I,mBAAmB,EAAE,kEAAkE;CACxF,CAAC,CAAA","sourcesContent":["/** Generated by release CI. Do not edit in a release checkout. */\nexport interface BluetoothSdkReleaseMetadata {\n schemaVersion: 1\n familyBaseVersion: string | null\n releaseIdentity: string | null\n releaseSetId: string | null\n sourceCommit: string | null\n otaManifestUrl: string | null\n otaManifestSha256: string | null\n}\n\nexport const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({\n \"schemaVersion\": 1,\n \"familyBaseVersion\": \"3.2.0\",\n \"releaseIdentity\": \"3.2.0-dev.193\",\n \"releaseSetId\": \"mentra-3.2.0-dev.193\",\n \"sourceCommit\": \"ca3b4bd4109d80253a3369cbf1ca8f9e86236fba\",\n \"otaManifestUrl\": \"https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.193.json\",\n \"otaManifestSha256\": \"b5201e1a9d926df220adfbf26cba934948d3f09ed81453c3600770a7618dd997\"\n})\n"]}
|
|
@@ -11,7 +11,7 @@ enum BluetoothSdkDefaults {
|
|
|
11
11
|
static let voiceActivityDetectionEnabled = false
|
|
12
12
|
static let loudnessGateEnabled = false
|
|
13
13
|
private static let infoSdkVersionKey = "MentraBluetoothSdkVersion"
|
|
14
|
-
private static let swiftPackageSdkVersion = "3.2.0-dev.
|
|
14
|
+
private static let swiftPackageSdkVersion = "3.2.0-dev.193"
|
|
15
15
|
private static let swiftPackageSdkVersionPlaceholder = "__MENTRA" + "_BLUETOOTH_SDK_VERSION__"
|
|
16
16
|
|
|
17
17
|
private static func normalizedSdkVersion(_ value: String?) -> String? {
|
package/ios/Source/Bridge.swift
CHANGED
|
@@ -74,6 +74,9 @@ class Bridge {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
static func log(_ message: String) {
|
|
77
|
+
// Native diagnostics and the JS console share this event. Do not capture
|
|
78
|
+
// stdout: React Native can write the forwarded message back to stdout.
|
|
79
|
+
NSLog("%@", message)
|
|
77
80
|
let data = ["message": message]
|
|
78
81
|
Bridge.sendTypedMessage("log", body: data)
|
|
79
82
|
}
|
|
@@ -664,5 +667,3 @@ class Bridge {
|
|
|
664
667
|
return payload
|
|
665
668
|
}
|
|
666
669
|
}
|
|
667
|
-
|
|
668
|
-
|
|
@@ -3,9 +3,9 @@ import Foundation
|
|
|
3
3
|
/// Generated by release CI. Do not edit in a release checkout.
|
|
4
4
|
enum GeneratedReleaseMetadata {
|
|
5
5
|
static let familyBaseVersion = "3.2.0"
|
|
6
|
-
static let releaseIdentity = "3.2.0-dev.
|
|
7
|
-
static let releaseSetId = "mentra-3.2.0-dev.
|
|
8
|
-
static let sourceCommit = "
|
|
9
|
-
static let otaManifestUrl = "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.
|
|
10
|
-
static let otaManifestSha256 = "
|
|
6
|
+
static let releaseIdentity = "3.2.0-dev.193"
|
|
7
|
+
static let releaseSetId = "mentra-3.2.0-dev.193"
|
|
8
|
+
static let sourceCommit = "ca3b4bd4109d80253a3369cbf1ca8f9e86236fba"
|
|
9
|
+
static let otaManifestUrl = "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.193.json"
|
|
10
|
+
static let otaManifestSha256 = "b5201e1a9d926df220adfbf26cba934948d3f09ed81453c3600770a7618dd997"
|
|
11
11
|
}
|
|
@@ -84,6 +84,9 @@ enum BleTraceLogger {
|
|
|
84
84
|
|
|
85
85
|
private static func emit(_ line: String) {
|
|
86
86
|
os_log("%{public}@", log: log, type: .info, line)
|
|
87
|
+
// Log events are excluded from tracing in Bridge, so forwarding a trace
|
|
88
|
+
// cannot recursively generate another trace.
|
|
89
|
+
Bridge.sendTypedMessage("log", body: ["message": line])
|
|
87
90
|
}
|
|
88
91
|
|
|
89
92
|
private static func format(
|
package/ios/Source/sgcs/G1.swift
CHANGED
|
@@ -1677,7 +1677,7 @@ extension G1 {
|
|
|
1677
1677
|
Task {
|
|
1678
1678
|
let success = await setBrightnessRaw(brightnessLevel, autoMode: autoMode)
|
|
1679
1679
|
if !success {
|
|
1680
|
-
|
|
1680
|
+
Bridge.log("Failed to set brightness to level \(level)% (mapped to \(mappedLevel))")
|
|
1681
1681
|
}
|
|
1682
1682
|
}
|
|
1683
1683
|
}
|
|
@@ -497,7 +497,7 @@ class BlePhotoUploadService {
|
|
|
497
497
|
|
|
498
498
|
request.httpBody = body
|
|
499
499
|
|
|
500
|
-
|
|
500
|
+
Bridge.log("LIVE: Uploading photo to webhook: \(webhookUrl)")
|
|
501
501
|
|
|
502
502
|
do {
|
|
503
503
|
let (data, response) = try await URLSession.shared.data(for: request)
|
|
@@ -513,7 +513,7 @@ class BlePhotoUploadService {
|
|
|
513
513
|
)
|
|
514
514
|
}
|
|
515
515
|
|
|
516
|
-
|
|
516
|
+
Bridge.log("LIVE: Upload successful. Response code: \(httpResponse.statusCode)")
|
|
517
517
|
return String(data: data, encoding: .utf8) ?? ""
|
|
518
518
|
|
|
519
519
|
} catch {
|
|
@@ -627,7 +627,7 @@ enum K900ProtocolUtils {
|
|
|
627
627
|
// Verify packet has enough data
|
|
628
628
|
let requiredLength = pos + Int(info.packSize) + LENGTH_FILE_VERIFY + LENGTH_FILE_END
|
|
629
629
|
if protocolData.count < requiredLength {
|
|
630
|
-
|
|
630
|
+
Bridge.log(
|
|
631
631
|
"K900ProtocolUtils: File packet too short for data. Need: \(requiredLength), Have: \(protocolData.count), packSize=\(info.packSize), pos=\(pos)"
|
|
632
632
|
)
|
|
633
633
|
return nil
|
|
@@ -656,11 +656,11 @@ enum K900ProtocolUtils {
|
|
|
656
656
|
info.isValid = (calculatedVerify == info.verifyCode)
|
|
657
657
|
|
|
658
658
|
if !info.isValid {
|
|
659
|
-
|
|
659
|
+
Bridge.log(
|
|
660
660
|
"K900ProtocolUtils: File packet checksum failed. Expected: \(String(format: "%02X", info.verifyCode)), Calculated: \(String(format: "%02X", calculatedVerify))"
|
|
661
661
|
)
|
|
662
662
|
} else if shouldLogFilePacket(info) {
|
|
663
|
-
|
|
663
|
+
Bridge.log(
|
|
664
664
|
"K900ProtocolUtils: File packet extracted successfully: index=\(info.packIndex), size=\(info.packSize), fileName=\(info.fileName)"
|
|
665
665
|
)
|
|
666
666
|
}
|
|
@@ -732,7 +732,7 @@ private struct FileTransferSession {
|
|
|
732
732
|
if isBesLie {
|
|
733
733
|
// BES lie detected: totalPackets = fileSize / 400
|
|
734
734
|
newTotalPackets = fileSize / Self.BES_HARDCODED_PACK_SIZE
|
|
735
|
-
|
|
735
|
+
Bridge.log(
|
|
736
736
|
"📦 BES Lie detected! fakeFileSize=\(fileSize), totalPackets=\(newTotalPackets), actualPackSize=\(actualPackSize)"
|
|
737
737
|
)
|
|
738
738
|
} else {
|
|
@@ -741,7 +741,7 @@ private struct FileTransferSession {
|
|
|
741
741
|
}
|
|
742
742
|
|
|
743
743
|
if newTotalPackets != totalPackets {
|
|
744
|
-
|
|
744
|
+
Bridge.log(
|
|
745
745
|
"📦 Recalculating totalPackets: \(totalPackets) -> \(newTotalPackets) (packSize=\(actualPackSize), fileSize=\(fileSize))"
|
|
746
746
|
)
|
|
747
747
|
totalPackets = newTotalPackets
|
|
@@ -793,7 +793,7 @@ private struct FileTransferSession {
|
|
|
793
793
|
// Calculate actual file size by summing all received packet sizes
|
|
794
794
|
let actualFileSize = receivedPackets.values.reduce(0) { $0 + $1.count }
|
|
795
795
|
|
|
796
|
-
|
|
796
|
+
Bridge.log(
|
|
797
797
|
"📦 Assembling file: headerFileSize=\(fileSize), actualFileSize=\(actualFileSize), totalPackets=\(totalPackets)"
|
|
798
798
|
)
|
|
799
799
|
|
|
@@ -2077,7 +2077,7 @@ class G1FontLoader {
|
|
|
2077
2077
|
fontMap[character] = FontGlyph(width: width, height: height)
|
|
2078
2078
|
}
|
|
2079
2079
|
|
|
2080
|
-
|
|
2080
|
+
Bridge.log("Hardcoded font data loaded successfully! \(fontMap.count) glyphs mapped.")
|
|
2081
2081
|
}
|
|
2082
2082
|
|
|
2083
2083
|
func getGlyph(_ character: Character) -> FontGlyph {
|
|
@@ -62,8 +62,7 @@ private func jlog(_ message: String) {
|
|
|
62
62
|
/// is fully booted (RN bridge up, Metro pull done if dev, etc).
|
|
63
63
|
@objc static func maybeAutoBenchmark() {
|
|
64
64
|
guard ProcessInfo.processInfo.environment["MENTRA_RUN_JSC_BENCH"] != nil else { return }
|
|
65
|
-
|
|
66
|
-
log: jscLog, type: .info)
|
|
65
|
+
jlog("🧪 MENTRA_RUN_JSC_BENCH set — auto-running benchmark in 5s")
|
|
67
66
|
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 5.0) {
|
|
68
67
|
runBenchmark()
|
|
69
68
|
}
|
|
@@ -12,7 +12,7 @@ final class MessageChunkReassembler {
|
|
|
12
12
|
cleanupTimedOutSessions()
|
|
13
13
|
|
|
14
14
|
guard fragCount > 0, fragIdx >= 0, fragIdx < fragCount else {
|
|
15
|
-
|
|
15
|
+
Bridge.log("MessageChunkReassembler: Dropping invalid binary fragment for msgId \(msgId)")
|
|
16
16
|
return nil
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -42,7 +42,7 @@ final class MessageChunkReassembler {
|
|
|
42
42
|
|
|
43
43
|
let reassembled = session.reassemble()
|
|
44
44
|
activeBinarySessions.removeValue(forKey: msgId)
|
|
45
|
-
|
|
45
|
+
Bridge.log("MessageChunkReassembler: Reassembled \(reassembled.count) bytes from \(fragCount) binary fragments")
|
|
46
46
|
return reassembled
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -54,12 +54,12 @@ final class MessageChunkReassembler {
|
|
|
54
54
|
info.chunkIndex >= 0,
|
|
55
55
|
info.chunkIndex < info.totalChunks
|
|
56
56
|
else {
|
|
57
|
-
|
|
57
|
+
Bridge.log("MessageChunkReassembler: Dropping invalid chunk metadata for \(info.chunkId)")
|
|
58
58
|
return nil
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
if let existing = activeSessions[info.chunkId], existing.totalChunks != info.totalChunks {
|
|
62
|
-
|
|
62
|
+
Bridge.log(
|
|
63
63
|
"MessageChunkReassembler: totalChunks mismatch for \(info.chunkId) (expected \(existing.totalChunks), got \(info.totalChunks)); resetting session"
|
|
64
64
|
)
|
|
65
65
|
activeSessions.removeValue(forKey: info.chunkId)
|
|
@@ -78,7 +78,7 @@ final class MessageChunkReassembler {
|
|
|
78
78
|
)
|
|
79
79
|
|
|
80
80
|
guard session.addChunk(index: info.chunkIndex, data: info.data) else {
|
|
81
|
-
|
|
81
|
+
Bridge.log("MessageChunkReassembler: Failed to add chunk \(info.chunkIndex) for \(info.chunkId)")
|
|
82
82
|
return nil
|
|
83
83
|
}
|
|
84
84
|
if isNewSession {
|
|
@@ -91,7 +91,7 @@ final class MessageChunkReassembler {
|
|
|
91
91
|
|
|
92
92
|
let reassembled = session.reassemble()
|
|
93
93
|
activeSessions.removeValue(forKey: info.chunkId)
|
|
94
|
-
|
|
94
|
+
Bridge.log("MessageChunkReassembler: Reassembled \(reassembled.count) bytes from \(info.totalChunks) chunks")
|
|
95
95
|
return reassembled
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -41,7 +41,7 @@ class MessageChunker {
|
|
|
41
41
|
let needsChunking = messageBytes > MESSAGE_SIZE_THRESHOLD
|
|
42
42
|
|
|
43
43
|
if needsChunking {
|
|
44
|
-
|
|
44
|
+
Bridge.log("MessageChunker: Message size \(messageBytes) exceeds threshold \(MESSAGE_SIZE_THRESHOLD), will chunk")
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
return needsChunking
|
|
@@ -56,7 +56,7 @@ class MessageChunker {
|
|
|
56
56
|
*/
|
|
57
57
|
static func createChunks(originalJson: String, messageId: Int64 = -1, wakeUp: Bool = false) -> [[String: Any]] {
|
|
58
58
|
guard let messageData = originalJson.data(using: .utf8) else {
|
|
59
|
-
|
|
59
|
+
Bridge.log("MessageChunker: Failed to convert message to data")
|
|
60
60
|
return []
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -68,12 +68,12 @@ class MessageChunker {
|
|
|
68
68
|
for chunkSize in stride(from: INITIAL_CHUNK_DATA_SIZE, through: MIN_CHUNK_DATA_SIZE, by: -1) {
|
|
69
69
|
let chunks = buildChunks(messageData, chunkId: chunkId, messageId: messageId, chunkSize: chunkSize)
|
|
70
70
|
if allChunksFit(chunks, wakeUp: wakeUp) {
|
|
71
|
-
|
|
71
|
+
Bridge.log("MessageChunker: Creating \(chunks.count) chunks for message of size \(totalBytes) bytes using \(chunkSize)-byte UTF-8 slices")
|
|
72
72
|
return chunks
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
Bridge.log("MessageChunker: Unable to create K900 chunks within \(MAX_PACKED_CHUNK_SIZE) bytes")
|
|
77
77
|
return []
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -101,7 +101,7 @@ class MessageChunker {
|
|
|
101
101
|
|
|
102
102
|
chunks.append(chunk)
|
|
103
103
|
|
|
104
|
-
|
|
104
|
+
Bridge.log("MessageChunker: Created chunk \(i)/\(totalChunks - 1) with \(chunkString.data(using: .utf8)?.count ?? 0) bytes")
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
return chunks
|
|
@@ -111,7 +111,7 @@ class MessageChunker {
|
|
|
111
111
|
for (index, chunk) in chunks.enumerated() {
|
|
112
112
|
let packedLength = packedK900Length(chunk, wakeUp: wakeUp && index == 0)
|
|
113
113
|
if packedLength == nil || packedLength! > MAX_PACKED_CHUNK_SIZE {
|
|
114
|
-
|
|
114
|
+
Bridge.log("MessageChunker: Chunk \(index) packed to \(packedLength ?? 0) bytes, exceeding \(MAX_PACKED_CHUNK_SIZE)")
|
|
115
115
|
return false
|
|
116
116
|
}
|
|
117
117
|
}
|
|
@@ -191,7 +191,7 @@ class MessageChunker {
|
|
|
191
191
|
let totalChunks = (json["total"] as? Int) ?? (json["n"] as? Int),
|
|
192
192
|
let data = (json["data"] as? String) ?? (json["d"] as? String)
|
|
193
193
|
else {
|
|
194
|
-
|
|
194
|
+
Bridge.log("MessageChunker: Failed to extract chunk info from JSON")
|
|
195
195
|
return nil
|
|
196
196
|
}
|
|
197
197
|
|
|
@@ -255,7 +255,7 @@ class MessageChunker {
|
|
|
255
255
|
)
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
|
|
258
|
+
Bridge.log("MessageChunker: Created \(fragments.count) binary fragments for \(payload.count) bytes")
|
|
259
259
|
return fragments
|
|
260
260
|
}
|
|
261
261
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
@testable import MentraBluetoothSDK
|
|
2
|
+
import XCTest
|
|
3
|
+
|
|
4
|
+
final class NativeLogTests: XCTestCase {
|
|
5
|
+
func testTracingAnEventDoesNotRecursivelyTraceTheLog() {
|
|
6
|
+
let delivered = expectation(description: "Trace and original event")
|
|
7
|
+
delivered.expectedFulfillmentCount = 2
|
|
8
|
+
delivered.assertForOverFulfill = true
|
|
9
|
+
var events: [String] = []
|
|
10
|
+
let sink = Bridge.addEventSink { event, _ in
|
|
11
|
+
events.append(event)
|
|
12
|
+
delivered.fulfill()
|
|
13
|
+
}
|
|
14
|
+
defer { Bridge.removeEventSink(sink) }
|
|
15
|
+
Bridge.sendTypedMessage("head_up", body: ["up": true])
|
|
16
|
+
wait(for: [delivered], timeout: 2)
|
|
17
|
+
XCTAssertEqual(events, ["log", "head_up"])
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
func testNativeDiagnosticsReachTheLogEventOnce() {
|
|
21
|
+
let delivered = expectation(description: "Native log event")
|
|
22
|
+
delivered.assertForOverFulfill = true
|
|
23
|
+
let sink = Bridge.addEventSink { event, body in
|
|
24
|
+
guard event == "log" else { return }
|
|
25
|
+
XCTAssertEqual(body["message"] as? String, "MessageChunker: diagnostic")
|
|
26
|
+
delivered.fulfill()
|
|
27
|
+
}
|
|
28
|
+
defer { Bridge.removeEventSink(sink) }
|
|
29
|
+
DispatchQueue.global().async {
|
|
30
|
+
Bridge.log("MessageChunker: diagnostic")
|
|
31
|
+
}
|
|
32
|
+
wait(for: [delivered], timeout: 2)
|
|
33
|
+
}
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import {createScanSession} from "./scanSession"
|
|
2
2
|
import {NativeModule, requireNativeModule} from "expo"
|
|
3
|
+
import {Platform} from "react-native"
|
|
4
|
+
|
|
5
|
+
import {installNativeLogConsole} from "./nativeLogConsole"
|
|
3
6
|
|
|
4
7
|
import {
|
|
5
8
|
BluetoothSettingsUpdate,
|
|
@@ -287,6 +290,7 @@ export type BluetoothSdkInternalModule = BluetoothSdkNativeModule
|
|
|
287
290
|
// This call loads the native module object from the JSI.
|
|
288
291
|
// NativeModule<BluetoothSdkModuleEvents> already extends EventEmitter<BluetoothSdkModuleEvents>
|
|
289
292
|
const NativeBluetoothSdkModule = requireNativeModule<BluetoothSdkNativeModule>("BluetoothSdk")
|
|
293
|
+
installNativeLogConsole(NativeBluetoothSdkModule, Platform.OS)
|
|
290
294
|
|
|
291
295
|
const DEFAULT_CONNECT_OPTIONS: Required<ConnectOptions> = {
|
|
292
296
|
saveAsDefault: true,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
type LogSource = {
|
|
2
|
+
addListener(event: "log", listener: (event: {message: string}) => void): {remove(): void}
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
// The native module survives Fast Refresh. Replace its previous subscription
|
|
6
|
+
// when JS reloads this module, rather than printing each event more than once.
|
|
7
|
+
const subscriptionKey = Symbol.for("mentra.bluetooth-sdk.native-log-console")
|
|
8
|
+
type SubscribedLogSource = LogSource & {[subscriptionKey]?: {remove(): void}}
|
|
9
|
+
|
|
10
|
+
const credentialPattern =
|
|
11
|
+
/(?<![A-Za-z0-9])(?:access[_-]?token|refresh[_-]?token|auth[_-]?token|id[_-]?token|client[_-]?secret|api[_-]?key)(?![A-Za-z0-9])|(?<![A-Za-z0-9])(?:token|password|secret|authorization|auth|key)(?![A-Za-z0-9])\s*["']?\s*(?:is\s*)?[:=]\s*["']?\S+|(?<![A-Za-z0-9])bearer(?![A-Za-z0-9])\s+\S+/i
|
|
12
|
+
|
|
13
|
+
export function nativeLogMessage(message: string): string {
|
|
14
|
+
return credentialPattern.test(message) ? "[REDACTED]" : message.slice(0, 16_384)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function installNativeLogConsole(source: SubscribedLogSource, platform: string): void {
|
|
18
|
+
source[subscriptionKey]?.remove()
|
|
19
|
+
source[subscriptionKey] = source.addListener("log", ({message}) => {
|
|
20
|
+
// Resolve console at delivery time so host console interception receives
|
|
21
|
+
// native diagnostics in the same stream as ordinary JS logs.
|
|
22
|
+
console.log(`[native:${platform}]`, nativeLogMessage(message))
|
|
23
|
+
})
|
|
24
|
+
}
|
|
@@ -12,9 +12,9 @@ export interface BluetoothSdkReleaseMetadata {
|
|
|
12
12
|
export const BLUETOOTH_SDK_RELEASE_METADATA: Readonly<BluetoothSdkReleaseMetadata> = Object.freeze({
|
|
13
13
|
"schemaVersion": 1,
|
|
14
14
|
"familyBaseVersion": "3.2.0",
|
|
15
|
-
"releaseIdentity": "3.2.0-dev.
|
|
16
|
-
"releaseSetId": "mentra-3.2.0-dev.
|
|
17
|
-
"sourceCommit": "
|
|
18
|
-
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.
|
|
19
|
-
"otaManifestSha256": "
|
|
15
|
+
"releaseIdentity": "3.2.0-dev.193",
|
|
16
|
+
"releaseSetId": "mentra-3.2.0-dev.193",
|
|
17
|
+
"sourceCommit": "ca3b4bd4109d80253a3369cbf1ca8f9e86236fba",
|
|
18
|
+
"otaManifestUrl": "https://github.com/Mentra-Community/MentraOS/releases/download/mentra-builds-v3.2.0/mentra-live-ota-3.2.0-dev.193.json",
|
|
19
|
+
"otaManifestSha256": "b5201e1a9d926df220adfbf26cba934948d3f09ed81453c3600770a7618dd997"
|
|
20
20
|
})
|