@mentra/bluetooth-sdk 3.2.0-dev.191 → 3.2.0-dev.195

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.
Files changed (80) hide show
  1. package/README.md +29 -0
  2. package/android/src/main/java/com/mentra/bluetoothsdk/Bridge.kt +1 -3
  3. package/android/src/main/java/com/mentra/bluetoothsdk/GeneratedReleaseMetadata.kt +5 -5
  4. package/android/src/main/java/com/mentra/bluetoothsdk/MentraLocalNetworkModule.kt +1 -1
  5. package/android/src/main/java/com/mentra/bluetoothsdk/debug/BleTraceLogger.kt +1 -1
  6. package/android/src/main/java/com/mentra/bluetoothsdk/otaserver/MentraOtaServerModule.kt +1 -1
  7. package/android/src/main/java/com/mentra/bluetoothsdk/photoreceiver/MentraPhotoReceiverModule.kt +1 -1
  8. package/android/src/main/java/com/mentra/bluetoothsdk/services/PhoneMic.kt +1 -1
  9. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Ar99.java +8 -8
  10. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/G1.kt +1 -1
  11. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/Mach1.kt +2 -2
  12. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLive.kt +1 -1
  13. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +1 -1
  14. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/MentraNex.kt +1 -1
  15. package/android/src/main/java/com/mentra/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +2 -2
  16. package/android/src/main/java/com/mentra/bluetoothsdk/stt/STTTools.kt +2 -2
  17. package/android/src/main/java/com/mentra/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +1 -1
  18. package/android/src/main/java/com/mentra/bluetoothsdk/stt/VadGateSpeechPolicy.kt +1 -1
  19. package/android/src/main/java/com/mentra/bluetoothsdk/tts/TTSTools.kt +1 -1
  20. package/android/src/main/java/com/mentra/bluetoothsdk/utils/AES.java +7 -8
  21. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BitmapJavaUtils.java +2 -2
  22. package/android/src/main/java/com/mentra/bluetoothsdk/utils/BlePhotoUploadService.java +34 -34
  23. package/android/src/main/java/com/mentra/bluetoothsdk/utils/G1Text.kt +1 -1
  24. package/android/src/main/java/com/mentra/bluetoothsdk/utils/IncidentLogBleUploadService.java +4 -4
  25. package/android/src/main/java/com/mentra/bluetoothsdk/utils/K900ProtocolUtils.java +34 -34
  26. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunkReassembler.java +10 -10
  27. package/android/src/main/java/com/mentra/bluetoothsdk/utils/MessageChunker.java +6 -6
  28. package/android/src/main/java/com/mentra/bluetoothsdk/utils/NativeLog.java +89 -0
  29. package/android/src/main/java/com/mentra/bluetoothsdk/utils/NexSGCUtils.kt +2 -2
  30. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +10 -10
  31. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +7 -7
  32. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/Lc3Player.java +16 -16
  33. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PCMAudioPlayer.java +26 -26
  34. package/android/src/main/java/com/mentra/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +1 -1
  35. package/android/src/test/java/com/mentra/bluetoothsdk/utils/NativeLogTest.kt +61 -0
  36. package/build/BluetoothSdk.types.d.ts +7 -0
  37. package/build/BluetoothSdk.types.d.ts.map +1 -1
  38. package/build/BluetoothSdk.types.js.map +1 -1
  39. package/build/_private/BluetoothSdkModule.d.ts.map +1 -1
  40. package/build/_private/BluetoothSdkModule.js +4 -0
  41. package/build/_private/BluetoothSdkModule.js.map +1 -1
  42. package/build/_private/nativeLogConsole.d.ts +17 -0
  43. package/build/_private/nativeLogConsole.d.ts.map +1 -0
  44. package/build/_private/nativeLogConsole.js +16 -0
  45. package/build/_private/nativeLogConsole.js.map +1 -0
  46. package/build/generated/releaseMetadata.js +5 -5
  47. package/build/generated/releaseMetadata.js.map +1 -1
  48. package/build/react/useGlassesConnection.d.ts +5 -0
  49. package/build/react/useGlassesConnection.d.ts.map +1 -1
  50. package/build/react/useGlassesConnection.js +2 -2
  51. package/build/react/useGlassesConnection.js.map +1 -1
  52. package/build/react/useMentraBluetooth.d.ts +5 -0
  53. package/build/react/useMentraBluetooth.d.ts.map +1 -1
  54. package/build/react/useMentraBluetooth.js +1 -0
  55. package/build/react/useMentraBluetooth.js.map +1 -1
  56. package/ios/BluetoothSdkModule.swift +2 -2
  57. package/ios/Source/BluetoothSdkDefaults.swift +1 -1
  58. package/ios/Source/Bridge.swift +3 -2
  59. package/ios/Source/DeviceManager.swift +4 -2
  60. package/ios/Source/GeneratedReleaseMetadata.swift +5 -5
  61. package/ios/Source/MentraBluetoothSDK.swift +10 -3
  62. package/ios/Source/internal/BleTraceLogger.swift +3 -0
  63. package/ios/Source/sgcs/G1.swift +1 -1
  64. package/ios/Source/sgcs/MentraLive.swift +28 -11
  65. package/ios/Source/stt/STTTools.swift +1 -1
  66. package/ios/Source/types/DeviceModels.swift +9 -1
  67. package/ios/Source/utils/G1Text.swift +1 -1
  68. package/ios/Source/utils/JSCExperiment.swift +1 -2
  69. package/ios/Source/utils/MessageChunkReassembler.swift +6 -6
  70. package/ios/Source/utils/MessageChunker.swift +8 -8
  71. package/ios/Source/utils/Models.swift +1 -1
  72. package/ios/Tests/MentraLiveConnectionOptionsTests.swift +30 -0
  73. package/ios/Tests/NativeLogTests.swift +34 -0
  74. package/package.json +1 -1
  75. package/src/BluetoothSdk.types.ts +7 -0
  76. package/src/_private/BluetoothSdkModule.ts +5 -0
  77. package/src/_private/nativeLogConsole.ts +24 -0
  78. package/src/generated/releaseMetadata.ts +5 -5
  79. package/src/react/useGlassesConnection.ts +7 -2
  80. package/src/react/useMentraBluetooth.ts +6 -0
@@ -1,6 +1,6 @@
1
1
  package com.mentra.bluetoothsdk.utils;
2
2
 
3
- import android.util.Log;
3
+ import com.mentra.bluetoothsdk.utils.NativeLog;
4
4
 
5
5
  import java.io.IOException;
6
6
  import java.util.concurrent.TimeUnit;
@@ -60,16 +60,16 @@ public final class IncidentLogBleUploadService {
60
60
  .build();
61
61
  try (Response response = client.newCall(request).execute()) {
62
62
  if (response.isSuccessful()) {
63
- Log.i(TAG, "Incident BLE relay upload OK: " + url);
63
+ NativeLog.i(TAG, "Incident BLE relay upload OK: " + url);
64
64
  callback.onDone(true, null);
65
65
  } else {
66
66
  String msg = "HTTP " + response.code();
67
- Log.e(TAG, "Incident BLE relay upload failed: " + msg + " " + url);
67
+ NativeLog.e(TAG, "Incident BLE relay upload failed: " + msg + " " + url);
68
68
  callback.onDone(false, msg);
69
69
  }
70
70
  }
71
71
  } catch (IOException e) {
72
- Log.e(TAG, "Incident BLE relay upload IO error", e);
72
+ NativeLog.e(TAG, "Incident BLE relay upload IO error", e);
73
73
  callback.onDone(false, e.getMessage());
74
74
  }
75
75
  }).start();
@@ -5,7 +5,7 @@ import android.content.Context;
5
5
  import android.content.Intent;
6
6
  import android.content.IntentFilter;
7
7
  import android.content.pm.PackageManager;
8
- import android.util.Log;
8
+ import com.mentra.bluetoothsdk.utils.NativeLog;
9
9
  import android.os.Build;
10
10
 
11
11
  import org.json.JSONException;
@@ -78,7 +78,7 @@ public class K900ProtocolUtils {
78
78
  return packDataCommand(jsonBytes, CMD_TYPE_STRING, endian);
79
79
 
80
80
  } catch (JSONException e) {
81
- android.util.Log.e("K900ProtocolUtils", "Error creating JSON wrapper", e);
81
+ com.mentra.bluetoothsdk.utils.NativeLog.e("K900ProtocolUtils", "Error creating JSON wrapper", e);
82
82
  return null;
83
83
  }
84
84
  }
@@ -190,7 +190,7 @@ public class K900ProtocolUtils {
190
190
  return packDataCommand(jsonBytes, CMD_TYPE_STRING, endian);
191
191
 
192
192
  } catch (JSONException e) {
193
- android.util.Log.e("K900ProtocolUtils", "Error creating JSON wrapper for K900", e);
193
+ com.mentra.bluetoothsdk.utils.NativeLog.e("K900ProtocolUtils", "Error creating JSON wrapper for K900", e);
194
194
  return null;
195
195
  }
196
196
  }
@@ -214,7 +214,7 @@ public class K900ProtocolUtils {
214
214
  */
215
215
  public static byte[] formatMessageForTransmission(String jsonData, K900LengthCodec.Endian endian) {
216
216
  try {
217
- android.util.Log.e("K900ProtocolUtils", "🔄 Formatting message: " + jsonData);
217
+ com.mentra.bluetoothsdk.utils.NativeLog.e("K900ProtocolUtils", "🔄 Formatting message: " + jsonData);
218
218
 
219
219
  // Validate that input is proper JSON
220
220
  new JSONObject(jsonData);
@@ -225,7 +225,7 @@ public class K900ProtocolUtils {
225
225
  wrapper.put(FIELD_V, 1); // Optional version field
226
226
  wrapper.put(FIELD_B, new JSONObject()); // Optional body field
227
227
  String wrappedJson = wrapper.toString();
228
- android.util.Log.e("K900ProtocolUtils", "🔄 After C-wrapping: " + wrappedJson);
228
+ com.mentra.bluetoothsdk.utils.NativeLog.e("K900ProtocolUtils", "🔄 After C-wrapping: " + wrappedJson);
229
229
 
230
230
  // Now format with BES2700 protocol
231
231
  byte[] result =
@@ -243,7 +243,7 @@ public class K900ProtocolUtils {
243
243
  return result;
244
244
 
245
245
  } catch (JSONException e) {
246
- android.util.Log.e("K900ProtocolUtils", "❌ Error in formatMessageForTransmission", e);
246
+ com.mentra.bluetoothsdk.utils.NativeLog.e("K900ProtocolUtils", "❌ Error in formatMessageForTransmission", e);
247
247
  // Fallback: if json is invalid, still try to pack it without validation
248
248
  return packJsonCommand(jsonData, endian);
249
249
  }
@@ -262,7 +262,7 @@ public class K900ProtocolUtils {
262
262
  wrapper.put(FIELD_C, content);
263
263
  return wrapper.toString();
264
264
  } catch (JSONException e) {
265
- android.util.Log.e("K900ProtocolUtils", "Error creating C-wrapped JSON", e);
265
+ com.mentra.bluetoothsdk.utils.NativeLog.e("K900ProtocolUtils", "Error creating C-wrapped JSON", e);
266
266
  return null;
267
267
  }
268
268
  }
@@ -374,17 +374,17 @@ public class K900ProtocolUtils {
374
374
  * @return Parsed JSON object or null if not valid protocol data or valid JSON
375
375
  */
376
376
  public static JSONObject processReceivedBytesToJson(byte[] data) {
377
- android.util.Log.d("K900ProtocolUtils", "Processing received bytes for JSON extraction");
377
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Processing received bytes for JSON extraction");
378
378
 
379
379
  // Check for null or too small data
380
380
  if (data == null || data.length < 7) {
381
- android.util.Log.d("K900ProtocolUtils", "Received data is null or too short to be valid protocol data");
381
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Received data is null or too short to be valid protocol data");
382
382
  return null;
383
383
  }
384
384
 
385
385
  // Verify if this is K900 protocol format (starts with ##)
386
386
  if (!isK900ProtocolFormat(data)) {
387
- android.util.Log.d("K900ProtocolUtils", "Not in K900 protocol format (missing ## markers)");
387
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Not in K900 protocol format (missing ## markers)");
388
388
  return null;
389
389
  }
390
390
 
@@ -399,25 +399,25 @@ public class K900ProtocolUtils {
399
399
  ? detected.length
400
400
  : ((data[3] & 0xFF) | ((data[4] & 0xFF) << 8));
401
401
 
402
- android.util.Log.d("K900ProtocolUtils", "Command type: 0x" + String.format("%02X", commandType) +
402
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Command type: 0x" + String.format("%02X", commandType) +
403
403
  ", Payload length: " + payloadLength);
404
404
 
405
405
  // Verify we have enough data and the right command type
406
406
  if (commandType != CMD_TYPE_STRING) {
407
- android.util.Log.d("K900ProtocolUtils", "Not a JSON/string command type (0x30), got: 0x" +
407
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Not a JSON/string command type (0x30), got: 0x" +
408
408
  String.format("%02X", commandType));
409
409
  return null;
410
410
  }
411
411
 
412
412
  if (data.length < payloadLength + 7) {
413
- android.util.Log.d("K900ProtocolUtils", "Received data size (" + data.length +
413
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Received data size (" + data.length +
414
414
  ") is less than expected size (" + (payloadLength + 7) + ")");
415
415
  return null;
416
416
  }
417
417
 
418
418
  // Check for end markers ($$)
419
419
  if (data[5 + payloadLength] != CMD_END_CODE[0] || data[6 + payloadLength] != CMD_END_CODE[1]) {
420
- android.util.Log.d("K900ProtocolUtils", "End markers ($$) not found where expected");
420
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "End markers ($$) not found where expected");
421
421
  return null;
422
422
  }
423
423
 
@@ -429,15 +429,15 @@ public class K900ProtocolUtils {
429
429
  String payloadStr;
430
430
  try {
431
431
  payloadStr = new String(payload, StandardCharsets.UTF_8);
432
- android.util.Log.d("K900ProtocolUtils", "Extracted JSON payload bytes: " + payloadLength);
432
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Extracted JSON payload bytes: " + payloadLength);
433
433
  } catch (Exception e) {
434
- android.util.Log.e("K900ProtocolUtils", "Error converting payload to string", e);
434
+ com.mentra.bluetoothsdk.utils.NativeLog.e("K900ProtocolUtils", "Error converting payload to string", e);
435
435
  return null;
436
436
  }
437
437
 
438
438
  // Check if it's valid JSON
439
439
  if (!payloadStr.startsWith("{") || !payloadStr.endsWith("}")) {
440
- android.util.Log.d("K900ProtocolUtils", "Payload is not valid JSON");
440
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Payload is not valid JSON");
441
441
  return null;
442
442
  }
443
443
 
@@ -448,14 +448,14 @@ public class K900ProtocolUtils {
448
448
  // Check if this is C-wrapped format {"C": "..."}
449
449
  if (json.has(FIELD_C)) {
450
450
  String innerContent = json.optString(FIELD_C, "");
451
- android.util.Log.d("K900ProtocolUtils", "Detected C-wrapped format");
451
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Detected C-wrapped format");
452
452
 
453
453
  // Try to parse the inner content as JSON
454
454
  try {
455
455
  JSONObject innerJson = new JSONObject(innerContent);
456
456
  return innerJson;
457
457
  } catch (JSONException e) {
458
- android.util.Log.d("K900ProtocolUtils", "Inner content is not JSON, returning outer JSON object");
458
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Inner content is not JSON, returning outer JSON object");
459
459
  // If inner content is not JSON, return the outer JSON
460
460
  return json;
461
461
  }
@@ -464,7 +464,7 @@ public class K900ProtocolUtils {
464
464
  return json;
465
465
  }
466
466
  } catch (JSONException e) {
467
- android.util.Log.e("K900ProtocolUtils", "Error parsing JSON payload: " + e.getMessage(), e);
467
+ com.mentra.bluetoothsdk.utils.NativeLog.e("K900ProtocolUtils", "Error parsing JSON payload: " + e.getMessage(), e);
468
468
  return null;
469
469
  }
470
470
  }
@@ -496,29 +496,29 @@ public class K900ProtocolUtils {
496
496
 
497
497
  // If looks like JSON but not C-wrapped, use the full formatting function
498
498
  if (originalData.startsWith("{") && !isCWrappedJson(originalData)) {
499
- android.util.Log.d("K900ProtocolUtils", "📦 JSON DATA BEFORE C-WRAPPING: " + originalData);
499
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "📦 JSON DATA BEFORE C-WRAPPING: " + originalData);
500
500
  byte[] formattedData = formatMessageForTransmission(originalData);
501
501
 
502
502
  // Debug log the formatting results if needed
503
- if (android.util.Log.isLoggable("K900ProtocolUtils", android.util.Log.DEBUG)) {
503
+ if (com.mentra.bluetoothsdk.utils.NativeLog.isLoggable("K900ProtocolUtils", com.mentra.bluetoothsdk.utils.NativeLog.DEBUG)) {
504
504
  StringBuilder hexDump = new StringBuilder();
505
505
  for (int i = 0; i < Math.min(formattedData.length, 50); i++) {
506
506
  hexDump.append(String.format("%02X ", formattedData[i]));
507
507
  }
508
- android.util.Log.d("K900ProtocolUtils", "📦 AFTER C-WRAPPING & PROTOCOL FORMATTING (first 50 bytes): " + hexDump.toString());
509
- android.util.Log.d("K900ProtocolUtils", "📦 Total formatted length: " + formattedData.length + " bytes");
508
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "📦 AFTER C-WRAPPING & PROTOCOL FORMATTING (first 50 bytes): " + hexDump.toString());
509
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "📦 Total formatted length: " + formattedData.length + " bytes");
510
510
  }
511
511
 
512
512
  return formattedData;
513
513
  } else {
514
514
  // Otherwise just apply protocol formatting
515
- android.util.Log.d("K900ProtocolUtils", "📦 Data already C-wrapped or not JSON: " + originalData);
516
- android.util.Log.d("K900ProtocolUtils", "Formatting data with K900 protocol (adding ##...)");
515
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "📦 Data already C-wrapped or not JSON: " + originalData);
516
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Formatting data with K900 protocol (adding ##...)");
517
517
  return packDataCommand(data, CMD_TYPE_STRING);
518
518
  }
519
519
  } catch (Exception e) {
520
520
  // If we can't interpret as string, just apply protocol formatting to raw bytes
521
- android.util.Log.d("K900ProtocolUtils", "Applying protocol format to raw bytes");
521
+ com.mentra.bluetoothsdk.utils.NativeLog.d("K900ProtocolUtils", "Applying protocol format to raw bytes");
522
522
  return packDataCommand(data, CMD_TYPE_STRING);
523
523
  }
524
524
  }
@@ -573,10 +573,10 @@ public class K900ProtocolUtils {
573
573
  String model = android.os.Build.MODEL.toLowerCase();
574
574
  return model.contains("k900") || model.contains("xyglasses");
575
575
  } catch (Exception e) {
576
- Log.e("K900ProtocolUtils", "Error checking for K900 specific broadcast", e);
576
+ NativeLog.e("K900ProtocolUtils", "Error checking for K900 specific broadcast", e);
577
577
  }
578
578
  } catch (Exception e) {
579
- Log.d("K900ProtocolUtils", "Not a K900 device: " + e.getMessage());
579
+ NativeLog.d("K900ProtocolUtils", "Not a K900 device: " + e.getMessage());
580
580
  }
581
581
 
582
582
  return false;
@@ -711,7 +711,7 @@ public class K900ProtocolUtils {
711
711
  */
712
712
  public static FilePacketInfo extractFilePacket(byte[] protocolData) {
713
713
  if (!isK900ProtocolFormat(protocolData) || protocolData.length < 31) {
714
- Log.e("K900ProtocolUtils", "extractFilePacket: Invalid format or too short. Length=" +
714
+ NativeLog.e("K900ProtocolUtils", "extractFilePacket: Invalid format or too short. Length=" +
715
715
  (protocolData != null ? protocolData.length : 0) +
716
716
  ", isK900Format=" + isK900ProtocolFormat(protocolData));
717
717
  return null;
@@ -757,7 +757,7 @@ public class K900ProtocolUtils {
757
757
 
758
758
  // Verify packet has enough data
759
759
  if (protocolData.length < pos + info.packSize + LENGTH_FILE_VERIFY + LENGTH_FILE_END) {
760
- Log.e("K900ProtocolUtils", "File packet too short for data. Need: " +
760
+ NativeLog.e("K900ProtocolUtils", "File packet too short for data. Need: " +
761
761
  (pos + info.packSize + LENGTH_FILE_VERIFY + LENGTH_FILE_END) +
762
762
  ", Have: " + protocolData.length +
763
763
  ", packSize=" + info.packSize + ", pos=" + pos);
@@ -788,13 +788,13 @@ public class K900ProtocolUtils {
788
788
  info.isValid = (calculatedVerify == info.verifyCode);
789
789
 
790
790
  if (!info.isValid) {
791
- Log.e("K900ProtocolUtils", "File packet checksum failed. Expected: " +
791
+ NativeLog.e("K900ProtocolUtils", "File packet checksum failed. Expected: " +
792
792
  String.format("%02X", info.verifyCode) + ", Calculated: " +
793
793
  String.format("%02X", calculatedVerify));
794
794
  } else if (info.packIndex == 0
795
795
  || info.packIndex % 32 == 0
796
796
  || info.packIndex == (info.fileSize + FILE_PACK_SIZE - 1) / FILE_PACK_SIZE - 1) {
797
- Log.d("K900ProtocolUtils", "File packet extracted successfully: index=" + info.packIndex +
797
+ NativeLog.d("K900ProtocolUtils", "File packet extracted successfully: index=" + info.packIndex +
798
798
  ", size=" + info.packSize + ", fileName=" + info.fileName);
799
799
  }
800
800
 
@@ -820,7 +820,7 @@ public class K900ProtocolUtils {
820
820
 
821
821
  return message.toString();
822
822
  } catch (JSONException e) {
823
- Log.e("K900ProtocolUtils", "Error creating file transfer ack", e);
823
+ NativeLog.e("K900ProtocolUtils", "Error creating file transfer ack", e);
824
824
  return null;
825
825
  }
826
826
  }
@@ -1,6 +1,6 @@
1
1
  package com.mentra.bluetoothsdk.utils;
2
2
 
3
- import android.util.Log;
3
+ import com.mentra.bluetoothsdk.utils.NativeLog;
4
4
 
5
5
  import java.util.Map;
6
6
  import java.util.concurrent.ConcurrentHashMap;
@@ -21,7 +21,7 @@ public class MessageChunkReassembler {
21
21
  cleanupTimedOutSessions();
22
22
 
23
23
  if (msgId < 0 || fragCount <= 0 || fragIdx < 0 || fragIdx >= fragCount || data == null) {
24
- Log.w(TAG, "Dropping invalid binary fragment: msgId=" + msgId + ", index=" + fragIdx
24
+ NativeLog.w(TAG, "Dropping invalid binary fragment: msgId=" + msgId + ", index=" + fragIdx
25
25
  + ", total=" + fragCount);
26
26
  return null;
27
27
  }
@@ -36,7 +36,7 @@ public class MessageChunkReassembler {
36
36
  return new BinarySession(msgId, fragCount);
37
37
  }
38
38
  if (existingSession.fragCount != fragCount) {
39
- Log.w(TAG, "fragCount mismatch for msgId " + msgId + ", resetting session");
39
+ NativeLog.w(TAG, "fragCount mismatch for msgId " + msgId + ", resetting session");
40
40
  return new BinarySession(msgId, fragCount);
41
41
  }
42
42
  return existingSession;
@@ -52,7 +52,7 @@ public class MessageChunkReassembler {
52
52
 
53
53
  byte[] reassembled = session.reassemble();
54
54
  activeBinarySessions.remove(msgId);
55
- Log.d(TAG, "Reassembled binary message of " + reassembled.length + " bytes from "
55
+ NativeLog.d(TAG, "Reassembled binary message of " + reassembled.length + " bytes from "
56
56
  + fragCount + " fragments");
57
57
  return reassembled;
58
58
  }
@@ -62,13 +62,13 @@ public class MessageChunkReassembler {
62
62
 
63
63
  if (chunkId == null || chunkId.isEmpty() || totalChunks <= 0 || chunkIndex < 0
64
64
  || chunkIndex >= totalChunks || data == null) {
65
- Log.w(TAG, "Dropping invalid chunk metadata: id=" + chunkId + ", index=" + chunkIndex
65
+ NativeLog.w(TAG, "Dropping invalid chunk metadata: id=" + chunkId + ", index=" + chunkIndex
66
66
  + ", total=" + totalChunks);
67
67
  return null;
68
68
  }
69
69
 
70
70
  if (activeSessions.size() >= MAX_CONCURRENT_SESSIONS && !activeSessions.containsKey(chunkId)) {
71
- Log.w(TAG, "Maximum concurrent chunk sessions reached, dropping oldest");
71
+ NativeLog.w(TAG, "Maximum concurrent chunk sessions reached, dropping oldest");
72
72
  removeOldestSession();
73
73
  }
74
74
 
@@ -77,7 +77,7 @@ public class MessageChunkReassembler {
77
77
  return new ChunkSession(chunkId, totalChunks);
78
78
  }
79
79
  if (existingSession.totalChunks != totalChunks) {
80
- Log.w(TAG, "totalChunks mismatch for " + chunkId + " (expected "
80
+ NativeLog.w(TAG, "totalChunks mismatch for " + chunkId + " (expected "
81
81
  + existingSession.totalChunks + ", got " + totalChunks + "), resetting session");
82
82
  return new ChunkSession(chunkId, totalChunks);
83
83
  }
@@ -85,11 +85,11 @@ public class MessageChunkReassembler {
85
85
  });
86
86
  boolean added = session.addChunk(chunkIndex, data);
87
87
  if (!added) {
88
- Log.w(TAG, "Failed to add chunk " + chunkIndex + " to session " + chunkId);
88
+ NativeLog.w(TAG, "Failed to add chunk " + chunkIndex + " to session " + chunkId);
89
89
  return null;
90
90
  }
91
91
 
92
- Log.d(TAG, "Added chunk " + chunkIndex + "/" + (totalChunks - 1) + " for session " + chunkId);
92
+ NativeLog.d(TAG, "Added chunk " + chunkIndex + "/" + (totalChunks - 1) + " for session " + chunkId);
93
93
 
94
94
  if (!session.isComplete()) {
95
95
  return null;
@@ -97,7 +97,7 @@ public class MessageChunkReassembler {
97
97
 
98
98
  String reassembled = session.reassemble();
99
99
  activeSessions.remove(chunkId);
100
- Log.d(TAG, "Reassembled message of " + reassembled.length() + " bytes from " + totalChunks + " chunks");
100
+ NativeLog.d(TAG, "Reassembled message of " + reassembled.length() + " bytes from " + totalChunks + " chunks");
101
101
  return reassembled;
102
102
  }
103
103
 
@@ -1,6 +1,6 @@
1
1
  package com.mentra.bluetoothsdk.utils;
2
2
 
3
- import android.util.Log;
3
+ import com.mentra.bluetoothsdk.utils.NativeLog;
4
4
 
5
5
  import org.json.JSONException;
6
6
  import org.json.JSONObject;
@@ -52,7 +52,7 @@ public class MessageChunker {
52
52
  boolean needsChunking = messageBytes > MESSAGE_SIZE_THRESHOLD;
53
53
 
54
54
  if (needsChunking) {
55
- Log.d(TAG, "Message size " + messageBytes + " exceeds threshold " + MESSAGE_SIZE_THRESHOLD + ", will chunk");
55
+ NativeLog.d(TAG, "Message size " + messageBytes + " exceeds threshold " + MESSAGE_SIZE_THRESHOLD + ", will chunk");
56
56
  }
57
57
 
58
58
  return needsChunking;
@@ -83,7 +83,7 @@ public class MessageChunker {
83
83
  for (int chunkSize = INITIAL_CHUNK_DATA_SIZE; chunkSize >= MIN_CHUNK_DATA_SIZE; chunkSize--) {
84
84
  List<JSONObject> chunks = buildChunks(messageBytes, chunkId, messageId, chunkSize);
85
85
  if (allChunksFit(chunks, wakeup)) {
86
- Log.d(TAG, "Creating " + chunks.size() + " chunks for message of size " + totalBytes
86
+ NativeLog.d(TAG, "Creating " + chunks.size() + " chunks for message of size " + totalBytes
87
87
  + " bytes using " + chunkSize + "-byte UTF-8 slices");
88
88
  return chunks;
89
89
  }
@@ -115,7 +115,7 @@ public class MessageChunker {
115
115
 
116
116
  chunks.add(chunk);
117
117
 
118
- Log.d(TAG, "Created chunk " + i + "/" + (totalChunks - 1) + " with " + chunkData.getBytes(StandardCharsets.UTF_8).length + " bytes");
118
+ NativeLog.d(TAG, "Created chunk " + i + "/" + (totalChunks - 1) + " with " + chunkData.getBytes(StandardCharsets.UTF_8).length + " bytes");
119
119
  }
120
120
 
121
121
  return chunks;
@@ -125,7 +125,7 @@ public class MessageChunker {
125
125
  for (int i = 0; i < chunks.size(); i++) {
126
126
  byte[] packed = K900ProtocolUtils.packJsonToK900(chunks.get(i).toString(), wakeup && i == 0);
127
127
  if (packed == null || packed.length > MAX_PACKED_CHUNK_SIZE) {
128
- Log.d(TAG, "Chunk " + i + " packed to " + (packed != null ? packed.length : 0)
128
+ NativeLog.d(TAG, "Chunk " + i + " packed to " + (packed != null ? packed.length : 0)
129
129
  + " bytes, exceeding " + MAX_PACKED_CHUNK_SIZE);
130
130
  return false;
131
131
  }
@@ -261,7 +261,7 @@ public class MessageChunker {
261
261
  fragments.add(new BinaryFragment(flags, msgId, i, fragCount, fragPayload));
262
262
  }
263
263
 
264
- Log.d(TAG, "Created " + fragments.size() + " binary fragments for "
264
+ NativeLog.d(TAG, "Created " + fragments.size() + " binary fragments for "
265
265
  + payload.length + " byte payload");
266
266
  return fragments;
267
267
  }
@@ -0,0 +1,89 @@
1
+ package com.mentra.bluetoothsdk.utils;
2
+
3
+ import com.mentra.bluetoothsdk.Bridge;
4
+ import java.util.HashMap;
5
+ import java.util.Map;
6
+
7
+ /** Writes SDK diagnostics to both the native console and the SDK's JavaScript log event. */
8
+ public final class NativeLog {
9
+ public static final int DEBUG = android.util.Log.DEBUG;
10
+
11
+ private NativeLog() {}
12
+
13
+ /** Preserves Android's per-tag log-level check for existing SDK callers. */
14
+ public static boolean isLoggable(String tag, int level) {
15
+ return android.util.Log.isLoggable(tag, level);
16
+ }
17
+
18
+ /** Emits a verbose diagnostic to both sinks. */
19
+ public static int v(String tag, String message) {
20
+ return write(android.util.Log.VERBOSE, tag, message, null);
21
+ }
22
+
23
+ /** Emits a verbose diagnostic with its stack trace to both sinks. */
24
+ public static int v(String tag, String message, Throwable error) {
25
+ return write(android.util.Log.VERBOSE, tag, message, error);
26
+ }
27
+
28
+ /** Emits a debug diagnostic to both sinks. */
29
+ public static int d(String tag, String message) {
30
+ return write(DEBUG, tag, message, null);
31
+ }
32
+
33
+ /** Emits a debug diagnostic with its stack trace to both sinks. */
34
+ public static int d(String tag, String message, Throwable error) {
35
+ return write(DEBUG, tag, message, error);
36
+ }
37
+
38
+ /** Emits an informational diagnostic to both sinks. */
39
+ public static int i(String tag, String message) {
40
+ return write(android.util.Log.INFO, tag, message, null);
41
+ }
42
+
43
+ /** Emits an informational diagnostic with its stack trace to both sinks. */
44
+ public static int i(String tag, String message, Throwable error) {
45
+ return write(android.util.Log.INFO, tag, message, error);
46
+ }
47
+
48
+ /** Emits a warning to both sinks. */
49
+ public static int w(String tag, String message) {
50
+ return write(android.util.Log.WARN, tag, message, null);
51
+ }
52
+
53
+ /** Emits a warning with its stack trace to both sinks. */
54
+ public static int w(String tag, String message, Throwable error) {
55
+ return write(android.util.Log.WARN, tag, message, error);
56
+ }
57
+
58
+ /** Emits an error to both sinks. */
59
+ public static int e(String tag, String message) {
60
+ return write(android.util.Log.ERROR, tag, message, null);
61
+ }
62
+
63
+ /** Emits an error with its stack trace to both sinks. */
64
+ public static int e(String tag, String message, Throwable error) {
65
+ return write(android.util.Log.ERROR, tag, message, error);
66
+ }
67
+
68
+ private static int write(int priority, String tag, String message, Throwable error) {
69
+ String text = String.valueOf(message);
70
+ if (error != null) text += "\n" + android.util.Log.getStackTraceString(error);
71
+ int result = android.util.Log.println(priority, tag, text);
72
+ Map<String, Object> body = new HashMap<>();
73
+ body.put("message", "[" + priorityLabel(priority) + "/" + tag + "] " + text);
74
+ // Dispatch directly: Bridge.log uses this logger too. Never tail logcat here,
75
+ // because React Native writes console output back to it.
76
+ Bridge.sendTypedMessage("log", body);
77
+ return result;
78
+ }
79
+
80
+ private static String priorityLabel(int priority) {
81
+ switch (priority) {
82
+ case android.util.Log.VERBOSE: return "V";
83
+ case android.util.Log.DEBUG: return "D";
84
+ case android.util.Log.WARN: return "W";
85
+ case android.util.Log.ERROR: return "E";
86
+ default: return "I";
87
+ }
88
+ }
89
+ }
@@ -5,7 +5,7 @@ import java.io.IOException
5
5
  import java.util.UUID
6
6
 
7
7
  import android.content.Context
8
- import android.util.Log
8
+ import com.mentra.bluetoothsdk.utils.NativeLog as Log
9
9
 
10
10
  import com.mentra.bluetoothsdk.Bridge
11
11
  import com.mentra.bluetoothsdk.DeviceStore
@@ -695,4 +695,4 @@ object NexEventUtils {
695
695
  Log.e(TAG, "NOTIF: Error sending ble command received event", e)
696
696
  }
697
697
  }
698
- }
698
+ }
@@ -1,6 +1,6 @@
1
1
  package com.mentra.bluetoothsdk.utils.audio;
2
2
 
3
- import android.util.Log;
3
+ import com.mentra.bluetoothsdk.utils.NativeLog;
4
4
 
5
5
  import java.util.Arrays;
6
6
  import java.util.concurrent.ArrayBlockingQueue;
@@ -76,7 +76,7 @@ public final class Ar99OpusPcmDecoder extends Thread {
76
76
  }
77
77
  byte[] payloadCopy = Arrays.copyOf(opusPayload, opusPayload.length);
78
78
  if (!taskQueue.offer(new Task(TaskType.DECODE, payloadCopy))) {
79
- Log.w(TAG, "AR99 Opus task queue full, drop notify len=" + payloadCopy.length);
79
+ NativeLog.w(TAG, "AR99 Opus task queue full, drop notify len=" + payloadCopy.length);
80
80
  }
81
81
  }
82
82
 
@@ -123,7 +123,7 @@ public final class Ar99OpusPcmDecoder extends Thread {
123
123
  }
124
124
  } catch (InterruptedException e) {
125
125
  if (running) {
126
- Log.w(TAG, "AR99 Opus worker interrupted: " + e.getMessage());
126
+ NativeLog.w(TAG, "AR99 Opus worker interrupted: " + e.getMessage());
127
127
  }
128
128
  } finally {
129
129
  taskQueue.clear();
@@ -150,7 +150,7 @@ public final class Ar99OpusPcmDecoder extends Thread {
150
150
  }
151
151
 
152
152
  if (buffer.length + chunk.length > MAX_BUFFER_SIZE) {
153
- Log.w(
153
+ NativeLog.w(
154
154
  TAG,
155
155
  "AR99 Opus buffer overflow, clear"
156
156
  + ", oldBuf="
@@ -179,7 +179,7 @@ public final class Ar99OpusPcmDecoder extends Thread {
179
179
  if (drop > 0) {
180
180
  totalResyncDropBytes += drop;
181
181
  headerLooksStable = false;
182
- Log.w(
182
+ NativeLog.w(
183
183
  TAG,
184
184
  "AR99 Opus resync drop(no header)"
185
185
  + " notify#="
@@ -200,7 +200,7 @@ public final class Ar99OpusPcmDecoder extends Thread {
200
200
  headerLooksStable = false;
201
201
  headerMismatch++;
202
202
  totalHeaderMismatchCount++;
203
- Log.w(
203
+ NativeLog.w(
204
204
  TAG,
205
205
  "AR99 Opus resync shift"
206
206
  + " notify#="
@@ -234,7 +234,7 @@ public final class Ar99OpusPcmDecoder extends Thread {
234
234
  totalHeaderMismatchCount++;
235
235
  headerLooksStable = false;
236
236
  if (totalFrameCount <= MAX_DIAG_FRAMES || !headerLooksStable) {
237
- Log.w(
237
+ NativeLog.w(
238
238
  TAG,
239
239
  "AR99 Opus frame header mismatch"
240
240
  + " frame#="
@@ -265,7 +265,7 @@ public final class Ar99OpusPcmDecoder extends Thread {
265
265
  decodeFail++;
266
266
  totalDecodeFailCount++;
267
267
  if (totalFrameCount <= MAX_DIAG_FRAMES || !headerLooksStable) {
268
- Log.w(
268
+ NativeLog.w(
269
269
  TAG,
270
270
  "AR99 Opus frame decode failed"
271
271
  + " frame#="
@@ -289,7 +289,7 @@ public final class Ar99OpusPcmDecoder extends Thread {
289
289
  || skipPayload > 0
290
290
  || pcmFrames == 0
291
291
  || !headerLooksStable) {
292
- Log.d(
292
+ NativeLog.d(
293
293
  TAG,
294
294
  "AR99 Opus decode summary"
295
295
  + " notify#="
@@ -337,7 +337,7 @@ public final class Ar99OpusPcmDecoder extends Thread {
337
337
  int bytes = samples * CHANNELS * 2;
338
338
  return Arrays.copyOf(pcm, bytes);
339
339
  } catch (OpusException | AssertionError | RuntimeException e) {
340
- Log.w(
340
+ NativeLog.w(
341
341
  TAG,
342
342
  "AR99 Opus decode failed len="
343
343
  + opusPayload.length
@@ -1,6 +1,6 @@
1
1
  package com.mentra.bluetoothsdk.utils.audio;
2
2
 
3
- import android.util.Log;
3
+ import com.mentra.bluetoothsdk.utils.NativeLog;
4
4
 
5
5
  import java.io.ByteArrayInputStream;
6
6
  import java.io.ByteArrayOutputStream;
@@ -132,7 +132,7 @@ public class ByteUtilAudioPlayer {
132
132
 
133
133
  if(str.length() + destoffset > dest.length)
134
134
  {
135
- Log.v("ByteUtilAudioPlayer","error string2Bytes src.length="+str.length()+",dest.length="+dest.length+",destoffset="+destoffset);
135
+ NativeLog.v("ByteUtilAudioPlayer","error string2Bytes src.length="+str.length()+",dest.length="+dest.length+",destoffset="+destoffset);
136
136
  return false;
137
137
  }
138
138
 
@@ -189,7 +189,7 @@ public class ByteUtilAudioPlayer {
189
189
  body = new String(bytes,offset, len,encode);
190
190
  body = body.trim();
191
191
  } catch (UnsupportedEncodingException e) {
192
- e.printStackTrace();
192
+ com.mentra.bluetoothsdk.utils.NativeLog.e("ByteUtilAudioPlayer", "Native exception", e);
193
193
  }
194
194
  return body;
195
195
  }
@@ -204,7 +204,7 @@ public class ByteUtilAudioPlayer {
204
204
 
205
205
  if(src.length < offset + len || dest.length < destoffset +len)
206
206
  {
207
- Log.v("ByteUtilAudioPlayer","error copyBytes src.length="+src.length+",dest.length="+dest.length);
207
+ NativeLog.v("ByteUtilAudioPlayer","error copyBytes src.length="+src.length+",dest.length="+dest.length);
208
208
  return false;
209
209
  }
210
210
  for(int i = 0; i<len; i++)
@@ -251,17 +251,17 @@ public class ByteUtilAudioPlayer {
251
251
  //BLog.v(CTConst.TAG,"copyBytes src="+src+",dest="+dest+",offset="+offset+",len="+len+",destoffset="+destoffset);
252
252
  if(src == null || dest == null)
253
253
  {
254
- Log.v("ByteUtilAudioPlayer","compareBytes error");
254
+ NativeLog.v("ByteUtilAudioPlayer","compareBytes error");
255
255
  return false;
256
256
  }
257
257
  if(len != destlen)
258
258
  {
259
- Log.v("ByteUtilAudioPlayer","error compareBytes len="+len+",destlen="+destlen);
259
+ NativeLog.v("ByteUtilAudioPlayer","error compareBytes len="+len+",destlen="+destlen);
260
260
  return false;
261
261
  }
262
262
  if(src.length < offset + len || dest.length < destoffset +destlen)
263
263
  {
264
- Log.v("ByteUtilAudioPlayer","error compareBytes src.length="+src.length+",dest.length="+dest.length);
264
+ NativeLog.v("ByteUtilAudioPlayer","error compareBytes src.length="+src.length+",dest.length="+dest.length);
265
265
  return false;
266
266
  }
267
267
  for(int i = 0; i<len; i++)