@glassly/bluetooth-sdk 0.1.0-dev.26
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 +563 -0
- package/android/build.gradle +450 -0
- package/android/gradle.properties.example +9 -0
- package/android/lc3Lib/CMakeLists.txt +6 -0
- package/android/lc3Lib/build.gradle +212 -0
- package/android/lc3Lib/proguard-rules.pro +33 -0
- package/android/lc3Lib/src/main/AndroidManifest.xml +2 -0
- package/android/lc3Lib/src/main/cpp/CMakeLists.txt +8 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/CMakeLists.txt +30 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/CMakeLists.txt +7 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.cc +96 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/jni/ogg_opus_encoder.h +53 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/Makefile.am +6 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h +26 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/config_types.h.in +26 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/ogg.h +209 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libogg/os_types.h +158 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus.h +981 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_custom.h +342 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_defines.h +799 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_multistream.h +660 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_projection.h +568 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/libopus/opus_types.h +166 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.cc +268 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/ogg_opus_encoder.h +115 -0
- package/android/lc3Lib/src/main/cpp/google_opus_stuff/opus_tools/opus_header.h +59 -0
- package/android/lc3Lib/src/main/cpp/liblc3/CMakeLists.txt +38 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/lc3.h +309 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/lc3_private.h +162 -0
- package/android/lc3Lib/src/main/cpp/liblc3/include/rnnoise.h +114 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/CMakeLists.txt +19 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.c +92 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/attdet.h +44 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.c +375 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bits.h +315 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.c +129 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/bwdet.h +69 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/common.h +148 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.c +70 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/energy.h +43 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/fastmath.h +158 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/lc3.c +702 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.c +893 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf.h +111 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_arm.h +506 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/ltpf_neon.h +281 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/makefile.mk +35 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.c +452 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct.h +57 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/mdct_neon.h +296 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/meson.build +46 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.c +61 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/plc.h +57 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.c +880 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/sns.h +103 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.c +904 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/spec.h +119 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.c +3457 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tables.h +94 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.c +457 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3/tns.h +99 -0
- package/android/lc3Lib/src/main/cpp/liblc3/liblc3.cpp +159 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/_kiss_fft_guts.h +182 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/arch.h +261 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.c +279 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/celt_lpc.h +59 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/common.h +48 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/compile.sh +3 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/denoise.c +646 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.c +601 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/kiss_fft.h +203 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/opus_types.h +159 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.c +526 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/pitch.h +149 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.c +178 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn.h +69 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.c +11051 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_data.h +34 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_reader.c +168 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/rnn_train.py +66 -0
- package/android/lc3Lib/src/main/cpp/liblc3/rnnoise/tansig_table.h +45 -0
- package/android/lc3Lib/src/main/java/com/glassly/lc3Lib/Lc3Cpp.java +38 -0
- package/android/lc3Lib/third_party/CMakeLists.txt +23 -0
- package/android/lc3Lib/third_party/CMakeLists_libogg.txt +18 -0
- package/android/lc3Lib/third_party/CMakeLists_libopus.txt +18 -0
- package/android/lc3Lib/third_party/CMakeLists_opus-tools.txt +23 -0
- package/android/lc3Lib/third_party/opus_tools/src/AUTHORS +5 -0
- package/android/lc3Lib/third_party/opus_tools/src/CMakeLists.txt +4 -0
- package/android/lc3Lib/third_party/opus_tools/src/COPYING +371 -0
- package/android/lc3Lib/third_party/opus_tools/src/ChangeLog +3 -0
- package/android/lc3Lib/third_party/opus_tools/src/Makefile.am +42 -0
- package/android/lc3Lib/third_party/opus_tools/src/Makefile.unix +23 -0
- package/android/lc3Lib/third_party/opus_tools/src/NEWS +0 -0
- package/android/lc3Lib/third_party/opus_tools/src/README +0 -0
- package/android/lc3Lib/third_party/opus_tools/src/autogen.sh +115 -0
- package/android/lc3Lib/third_party/opus_tools/src/configure.ac +293 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/arch.h +239 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/audio-in.c +1046 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.c +245 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/diag_range.h +28 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.c +320 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/info_opus.h +51 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/lpc.c +157 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/lpc.h +27 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.c +286 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opus_header.h +59 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusdec.c +884 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.c +1021 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusenc.h +101 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.c +639 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/opusinfo.h +51 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/os_support.h +167 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/resample.c +1137 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/speex_resampler.h +344 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/stack_alloc.h +115 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.c +125 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wav_io.h +62 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.c +223 -0
- package/android/lc3Lib/third_party/opus_tools/src/src/wave_out.h +60 -0
- package/android/settings.gradle +4 -0
- package/android/silero/LICENSE.md +17 -0
- package/android/silero/build.gradle +50 -0
- package/android/silero/proguard-rules.pro +24 -0
- package/android/silero/src/androidTest/assets/hello.wav +0 -0
- package/android/silero/src/androidTest/java/com/konovalov/vad/silero/VadSileroTest.kt +79 -0
- package/android/silero/src/main/assets/LICENSE +21 -0
- package/android/silero/src/main/assets/silero_vad.onnx +0 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/Vad.kt +160 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/VadSilero.kt +449 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/FrameSize.kt +15 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/Mode.kt +14 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/config/SampleRate.kt +12 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/utils/AudioUtils.kt +47 -0
- package/android/silero/src/main/java/com/konovalov/vad/silero/utils/TensorMap.kt +41 -0
- package/android/silero/src/test/java/com/konovalov/vad/silero/utils/AudioUtilsTest.kt +44 -0
- package/android/src/main/AndroidManifest.xml +42 -0
- package/android/src/main/assets/G2Firmware/README.md +11 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdk.kt +2325 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkAnalytics.kt +216 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDebug.kt +14 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkDefaults.kt +7 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/BluetoothSdkModule.kt +1088 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/Bridge.kt +953 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceManager.kt +2528 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/DeviceStore.kt +360 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedChangelogCatalog.kt +8 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/GeneratedReleaseMetadata.kt +11 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/LocalNetworkModule.kt +414 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/ObservableStore.kt +88 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/OtaManifest.kt +191 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/ReleaseChangelog.kt +61 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/ScopedNetworkReadiness.kt +17 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/audio/AudioModels.kt +144 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/camera/CameraModels.kt +486 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/connection/ConnectionModels.kt +78 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/controllers/ControllerManager.kt +158 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/controllers/R1.kt +937 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/debug/BleTraceLogger.kt +165 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/events/BluetoothEvents.kt +178 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/internal/MapParsing.kt +140 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/net/LocalIpv4.kt +57 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServer.kt +304 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/otaserver/OtaServerModule.kt +120 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistry.java +108 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoUploadServer.kt +622 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/photoreceiver/PhotoReceiverModule.kt +156 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/requests/DisplayRequests.kt +56 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/services/Foreground.kt +272 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/services/PhoneMic.kt +1195 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Ar99.java +2653 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuard.kt +49 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapper.kt +27 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1.kt +3878 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizer.kt +38 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2.kt +6428 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassification.kt +49 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwRenderer.kt +1241 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCache.kt +270 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocol.kt +291 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/G2Flasher.kt +411 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/GlasslyBle.java +43189 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Mach1.kt +1280 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLive.kt +10817 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLiveL2capChannel.kt +274 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisement.kt +54 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/MentraNex.kt +2011 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Nimo.kt +2532 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/SGCManager.kt +470 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/Simulated.kt +228 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/Ar99OtaManager.java +566 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaByteUtils.java +18 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCommandConstants.java +35 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaCrc32Util.java +13 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaGattTransport.java +11 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/sgcs/ar99/ota/OtaProtocol.java +92 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/speakerid/SpeakerIdEngine.kt +525 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/status/DeviceStatus.kt +669 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/status/RuntimeState.kt +201 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/status/WifiHotspotStatus.kt +189 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/streaming/StreamModels.kt +518 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/STTTools.kt +391 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/SherpaOnnxTranscriber.kt +518 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/stt/VadGateSpeechPolicy.kt +229 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/tts/TTSTools.kt +248 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/types/DeviceModels.kt +96 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/AES.java +106 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/AudioSessionMonitor.kt +189 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/AvifExifStripper.java +573 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BitmapJavaUtils.java +169 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleJsonCompact.java +496 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadService.java +677 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/BleWireProtocol.java +108 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/Constants.kt +46 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/G1Text.kt +436 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/HeifExifTagReader.java +199 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleRelayNaming.java +29 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/IncidentLogBleUploadService.java +77 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900LengthCodec.java +115 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtils.java +827 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunkReassembler.java +228 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/MessageChunker.java +325 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/NexSGCUtils.kt +698 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitor.kt +348 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/SmartGlassesConnectionState.java +9 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Ar99OpusPcmDecoder.java +398 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/ByteUtilAudioPlayer.java +655 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/Lc3Player.java +441 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PCMAudioPlayer.java +431 -0
- package/android/src/main/java/com/glassly/bluetoothsdk/utils/audio/PcmStreamPlayer.kt +328 -0
- package/android/src/main/res/values/strings.xml +4 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/BluetoothSdkExceptionTest.kt +18 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/OtaManifestDowngradeTest.kt +86 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/OtaStartResponsePolicyTest.kt +30 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/ReleaseChangelogTest.kt +13 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/ScopedNetworkReadinessTest.kt +39 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/StreamStatusEventTest.kt +29 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/camera/PhotoRequestTest.kt +141 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/otaserver/LocalOtaServerTest.kt +82 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/photoreceiver/LocalPhotoReceiverRegistryTest.java +75 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/services/ForegroundServiceTypeTest.kt +69 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/Ar99AdvertisementMatcherTest.java +22 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaHeartbeatGuardTest.kt +73 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/BesOtaProgressMapperTest.kt +67 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/EvenRleTest.kt +63 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G1TextSanitizerTest.kt +31 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwClassificationTest.kt +89 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwRendererTest.kt +344 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2CfwTextureCacheTest.kt +200 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/G2FlashProtocolTest.kt +131 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt +39 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLivePairingAdvertisementParserTest.kt +133 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/NimoProtocolTest.kt +333 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/AvifExifStripperTest.java +46 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/BinaryMessageChunkerTest.java +84 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/BleJsonCompactTest.java +319 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/BlePhotoUploadServiceTest.java +107 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsEndiannessTest.java +172 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/K900ProtocolUtilsTest.java +24 -0
- package/android/src/test/java/com/glassly/bluetoothsdk/utils/PhoneAudioMonitorTest.kt +45 -0
- package/android/src/test/resources/avif_with_exif.avif +0 -0
- package/app.plugin.js +3 -0
- package/build/BluetoothSdk.types.d.ts +1354 -0
- package/build/BluetoothSdk.types.d.ts.map +1 -0
- package/build/BluetoothSdk.types.js +41 -0
- package/build/BluetoothSdk.types.js.map +1 -0
- package/build/_internal.d.ts +17 -0
- package/build/_internal.d.ts.map +1 -0
- package/build/_internal.js +16 -0
- package/build/_internal.js.map +1 -0
- package/build/_private/BluetoothSdkModule.d.ts +172 -0
- package/build/_private/BluetoothSdkModule.d.ts.map +1 -0
- package/build/_private/BluetoothSdkModule.js +300 -0
- package/build/_private/BluetoothSdkModule.js.map +1 -0
- package/build/_private/LocalNetworkModule.d.ts +38 -0
- package/build/_private/LocalNetworkModule.d.ts.map +1 -0
- package/build/_private/LocalNetworkModule.js +3 -0
- package/build/_private/LocalNetworkModule.js.map +1 -0
- package/build/_private/cameraRequestPayload.d.ts +4 -0
- package/build/_private/cameraRequestPayload.d.ts.map +1 -0
- package/build/_private/cameraRequestPayload.js +32 -0
- package/build/_private/cameraRequestPayload.js.map +1 -0
- package/build/_private/photoRequestPayload.d.ts +6 -0
- package/build/_private/photoRequestPayload.d.ts.map +1 -0
- package/build/_private/photoRequestPayload.js +86 -0
- package/build/_private/photoRequestPayload.js.map +1 -0
- package/build/_private/sdkOtaManifest.d.ts +11 -0
- package/build/_private/sdkOtaManifest.d.ts.map +1 -0
- package/build/_private/sdkOtaManifest.js +15 -0
- package/build/_private/sdkOtaManifest.js.map +1 -0
- package/build/changelogs.d.ts +7 -0
- package/build/changelogs.d.ts.map +1 -0
- package/build/changelogs.js +46 -0
- package/build/changelogs.js.map +1 -0
- package/build/debug.d.ts +3 -0
- package/build/debug.d.ts.map +1 -0
- package/build/debug.js +8 -0
- package/build/debug.js.map +1 -0
- package/build/generated/changelogCatalog.d.ts +9 -0
- package/build/generated/changelogCatalog.d.ts.map +1 -0
- package/build/generated/changelogCatalog.js +12 -0
- package/build/generated/changelogCatalog.js.map +1 -0
- package/build/generated/releaseMetadata.d.ts +12 -0
- package/build/generated/releaseMetadata.d.ts.map +1 -0
- package/build/generated/releaseMetadata.js +10 -0
- package/build/generated/releaseMetadata.js.map +1 -0
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +171 -0
- package/build/index.js.map +1 -0
- package/build/ota-server/OtaServer.types.d.ts +14 -0
- package/build/ota-server/OtaServer.types.d.ts.map +1 -0
- package/build/ota-server/OtaServer.types.js +2 -0
- package/build/ota-server/OtaServer.types.js.map +1 -0
- package/build/ota-server/OtaServerModule.d.ts +21 -0
- package/build/ota-server/OtaServerModule.d.ts.map +1 -0
- package/build/ota-server/OtaServerModule.js +3 -0
- package/build/ota-server/OtaServerModule.js.map +1 -0
- package/build/ota-server/OtaServerModule.web.d.ts +10 -0
- package/build/ota-server/OtaServerModule.web.d.ts.map +1 -0
- package/build/ota-server/OtaServerModule.web.js +12 -0
- package/build/ota-server/OtaServerModule.web.js.map +1 -0
- package/build/ota-server/index.d.ts +3 -0
- package/build/ota-server/index.d.ts.map +1 -0
- package/build/ota-server/index.js +3 -0
- package/build/ota-server/index.js.map +1 -0
- package/build/ota-transport/index.d.ts +67 -0
- package/build/ota-transport/index.d.ts.map +1 -0
- package/build/ota-transport/index.js +58 -0
- package/build/ota-transport/index.js.map +1 -0
- package/build/photo-receiver/PhotoReceiver.types.d.ts +18 -0
- package/build/photo-receiver/PhotoReceiver.types.d.ts.map +1 -0
- package/build/photo-receiver/PhotoReceiver.types.js +2 -0
- package/build/photo-receiver/PhotoReceiver.types.js.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.d.ts +10 -0
- package/build/photo-receiver/PhotoReceiverModule.d.ts.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.js +3 -0
- package/build/photo-receiver/PhotoReceiverModule.js.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.web.d.ts +10 -0
- package/build/photo-receiver/PhotoReceiverModule.web.d.ts.map +1 -0
- package/build/photo-receiver/PhotoReceiverModule.web.js +12 -0
- package/build/photo-receiver/PhotoReceiverModule.web.js.map +1 -0
- package/build/photo-receiver/index.d.ts +3 -0
- package/build/photo-receiver/index.d.ts.map +1 -0
- package/build/photo-receiver/index.js +3 -0
- package/build/photo-receiver/index.js.map +1 -0
- package/build/react/index.d.ts +5 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +4 -0
- package/build/react/index.js.map +1 -0
- package/build/react/useBluetooth.d.ts +100 -0
- package/build/react/useBluetooth.d.ts.map +1 -0
- package/build/react/useBluetooth.js +167 -0
- package/build/react/useBluetooth.js.map +1 -0
- package/build/react/useBluetoothEvent.d.ts +6 -0
- package/build/react/useBluetoothEvent.d.ts.map +1 -0
- package/build/react/useBluetoothEvent.js +21 -0
- package/build/react/useBluetoothEvent.js.map +1 -0
- package/build/react/useBluetoothScan.d.ts +22 -0
- package/build/react/useBluetoothScan.d.ts.map +1 -0
- package/build/react/useBluetoothScan.js +135 -0
- package/build/react/useBluetoothScan.js.map +1 -0
- package/build/react/useBluetoothStatus.d.ts +16 -0
- package/build/react/useBluetoothStatus.d.ts.map +1 -0
- package/build/react/useBluetoothStatus.js +137 -0
- package/build/react/useBluetoothStatus.js.map +1 -0
- package/build/react/useGlassesConnection.d.ts +29 -0
- package/build/react/useGlassesConnection.d.ts.map +1 -0
- package/build/react/useGlassesConnection.js +153 -0
- package/build/react/useGlassesConnection.js.map +1 -0
- package/build/types/index.d.ts +3 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/index.js +2 -0
- package/build/types/index.js.map +1 -0
- package/expo-module.config.json +14 -0
- package/ios/BackgroundOtaArtifactDownloader.swift +125 -0
- package/ios/BluetoothSdkModule.swift +1034 -0
- package/ios/GlasslyBluetoothSDK.podspec +106 -0
- package/ios/LocalIPv4.swift +150 -0
- package/ios/LocalOtaServer.swift +309 -0
- package/ios/LocalPhotoUploadServer.swift +754 -0
- package/ios/OtaServerModule.swift +147 -0
- package/ios/Packages/CoreObjC/CoreObjC.xcodeproj/project.pbxproj +213 -0
- package/ios/Packages/CoreObjC/PcmConverter.h +22 -0
- package/ios/Packages/CoreObjC/PcmConverter.m +266 -0
- package/ios/Packages/CoreObjC/attdet.c +92 -0
- package/ios/Packages/CoreObjC/attdet.h +44 -0
- package/ios/Packages/CoreObjC/bits.c +375 -0
- package/ios/Packages/CoreObjC/bits.h +315 -0
- package/ios/Packages/CoreObjC/bwdet.c +129 -0
- package/ios/Packages/CoreObjC/bwdet.h +69 -0
- package/ios/Packages/CoreObjC/common.h +151 -0
- package/ios/Packages/CoreObjC/energy.c +70 -0
- package/ios/Packages/CoreObjC/energy.h +43 -0
- package/ios/Packages/CoreObjC/fastmath.h +158 -0
- package/ios/Packages/CoreObjC/include/PcmConverter.h +22 -0
- package/ios/Packages/CoreObjC/lc3.c +704 -0
- package/ios/Packages/CoreObjC/lc3.h +313 -0
- package/ios/Packages/CoreObjC/lc3_cpp.h +283 -0
- package/ios/Packages/CoreObjC/lc3_private.h +163 -0
- package/ios/Packages/CoreObjC/ltpf.c +905 -0
- package/ios/Packages/CoreObjC/ltpf.h +111 -0
- package/ios/Packages/CoreObjC/ltpf_arm.h +506 -0
- package/ios/Packages/CoreObjC/ltpf_neon.h +281 -0
- package/ios/Packages/CoreObjC/makefile.mk +35 -0
- package/ios/Packages/CoreObjC/mdct.c +469 -0
- package/ios/Packages/CoreObjC/mdct.h +57 -0
- package/ios/Packages/CoreObjC/mdct_neon.h +296 -0
- package/ios/Packages/CoreObjC/meson.build +61 -0
- package/ios/Packages/CoreObjC/plc.c +61 -0
- package/ios/Packages/CoreObjC/plc.h +57 -0
- package/ios/Packages/CoreObjC/rnnoise.h +114 -0
- package/ios/Packages/CoreObjC/sns.c +880 -0
- package/ios/Packages/CoreObjC/sns.h +103 -0
- package/ios/Packages/CoreObjC/spec.c +907 -0
- package/ios/Packages/CoreObjC/spec.h +119 -0
- package/ios/Packages/CoreObjC/tables.c +3457 -0
- package/ios/Packages/CoreObjC/tables.h +94 -0
- package/ios/Packages/CoreObjC/tns.c +457 -0
- package/ios/Packages/CoreObjC/tns.h +99 -0
- package/ios/Packages/SherpaOnnx/Model/joiner.onnx +0 -0
- package/ios/Packages/SherpaOnnx/Model/tokens.txt +502 -0
- package/ios/Packages/SherpaOnnx/SherpaOnnx.swift +2383 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/Info.plist +48 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64/libsherpa-onnx.a +0 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/c-api.h +4689 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/Headers/sherpa-onnx/c-api/cxx-api.h +2036 -0
- package/ios/Packages/SherpaOnnx/sherpa-onnx.xcframework/ios-arm64_x86_64-simulator/libsherpa-onnx.a +0 -0
- package/ios/Packages/libbz2/module.modulemap +5 -0
- package/ios/Packages/libbz2/shim.h +1 -0
- package/ios/PhotoReceiverModule.swift +129 -0
- package/ios/Source/BluetoothSDK.swift +2303 -0
- package/ios/Source/BluetoothSDKDebug.swift +12 -0
- package/ios/Source/BluetoothSdkDefaults.swift +29 -0
- package/ios/Source/Bridge.swift +708 -0
- package/ios/Source/Bridging-Header.h +16 -0
- package/ios/Source/DeviceManager.swift +2109 -0
- package/ios/Source/DeviceStore.swift +336 -0
- package/ios/Source/GeneratedChangelogCatalog.swift +7 -0
- package/ios/Source/GeneratedReleaseMetadata.swift +11 -0
- package/ios/Source/ObservableStore.swift +118 -0
- package/ios/Source/OtaManifest.swift +211 -0
- package/ios/Source/PrivacyInfo.xcprivacy +23 -0
- package/ios/Source/ReleaseChangelog.swift +65 -0
- package/ios/Source/Resources/G2Firmware/README.md +10 -0
- package/ios/Source/audio/AudioModels.swift +178 -0
- package/ios/Source/audio/PcmStreamPlayer.swift +410 -0
- package/ios/Source/camera/CameraModels.swift +866 -0
- package/ios/Source/connection/ScanSession.swift +27 -0
- package/ios/Source/controllers/ControllerManager.swift +184 -0
- package/ios/Source/controllers/R1.swift +829 -0
- package/ios/Source/errors/BluetoothSdkError.swift +19 -0
- package/ios/Source/events/BluetoothEvents.swift +342 -0
- package/ios/Source/internal/BleTraceLogger.swift +192 -0
- package/ios/Source/internal/BluetoothAvailability.swift +78 -0
- package/ios/Source/internal/BluetoothSdkAnalytics.swift +199 -0
- package/ios/Source/internal/ValueParsing.swift +99 -0
- package/ios/Source/requests/DisplayRequests.swift +82 -0
- package/ios/Source/services/PhoneMic.swift +662 -0
- package/ios/Source/sgcs/Ar99.swift +2451 -0
- package/ios/Source/sgcs/Frame.swift +649 -0
- package/ios/Source/sgcs/G1.swift +2554 -0
- package/ios/Source/sgcs/G2.swift +6887 -0
- package/ios/Source/sgcs/G2CfwClassification.swift +43 -0
- package/ios/Source/sgcs/G2CfwRenderer.swift +1279 -0
- package/ios/Source/sgcs/G2CfwTextureCache.swift +233 -0
- package/ios/Source/sgcs/G2FlashProtocol.swift +312 -0
- package/ios/Source/sgcs/G2Flasher.swift +773 -0
- package/ios/Source/sgcs/Mach1.swift +541 -0
- package/ios/Source/sgcs/MentraLive.swift +7011 -0
- package/ios/Source/sgcs/MentraLiveL2capChannel.swift +183 -0
- package/ios/Source/sgcs/MentraLivePairingAdvertisement.swift +64 -0
- package/ios/Source/sgcs/MentraNex.swift +3221 -0
- package/ios/Source/sgcs/Nimo.swift +1886 -0
- package/ios/Source/sgcs/SGCManager.swift +608 -0
- package/ios/Source/sgcs/Simulated.swift +274 -0
- package/ios/Source/sgcs/ar99/ota/Ar99OtaManager.swift +637 -0
- package/ios/Source/sgcs/glassly_ble.pb.swift +4354 -0
- package/ios/Source/speakerid/SpeakerIdEngine.swift +573 -0
- package/ios/Source/status/DeviceStatus.swift +981 -0
- package/ios/Source/status/RuntimeState.swift +359 -0
- package/ios/Source/status/WifiHotspotStatus.swift +345 -0
- package/ios/Source/streaming/StreamModels.swift +693 -0
- package/ios/Source/stt/STTTools.swift +191 -0
- package/ios/Source/stt/SherpaOnnxTranscriber.swift +466 -0
- package/ios/Source/tts/TTSTools.swift +171 -0
- package/ios/Source/types/DeviceModels.swift +177 -0
- package/ios/Source/utils/AudioSessionMonitor.swift +283 -0
- package/ios/Source/utils/BinaryCodec.swift +61 -0
- package/ios/Source/utils/BleJsonCompact.swift +405 -0
- package/ios/Source/utils/BleWireProtocol.swift +92 -0
- package/ios/Source/utils/Constants.swift +78 -0
- package/ios/Source/utils/Enums.swift +95 -0
- package/ios/Source/utils/G1Text.swift +2096 -0
- package/ios/Source/utils/JSCExperiment.swift +241 -0
- package/ios/Source/utils/MemoryMonitor.swift +44 -0
- package/ios/Source/utils/MessageChunkReassembler.swift +190 -0
- package/ios/Source/utils/MessageChunker.swift +294 -0
- package/ios/Source/utils/Models.swift +135 -0
- package/ios/Source/utils/PhoneAudioMonitor.swift +230 -0
- package/ios/Source/utils/TarBz2Extractor.swift +257 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwClassificationTests.swift +78 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwRendererTests.swift +372 -0
- package/ios/Tests/BluetoothSDKTests/G2CfwTextureCacheTests.swift +162 -0
- package/ios/Tests/BluetoothSDKTests/G2FlashProtocolTests.swift +182 -0
- package/ios/Tests/MentraLivePairingAdvertisementTests.swift +160 -0
- package/ios/Tests/ReleaseChangelogTests.swift +13 -0
- package/package.json +129 -0
- package/plugin/build/index.d.ts +10 -0
- package/plugin/build/index.js +12 -0
- package/plugin/build/withAndroid.d.ts +3 -0
- package/plugin/build/withAndroid.js +190 -0
- package/plugin/build/withIos.d.ts +5 -0
- package/plugin/build/withIos.js +108 -0
- package/scripts/inject-ios-sdk-version.mjs +65 -0
- package/scripts/public-ota-api.test.mjs +37 -0
- package/scripts/verify-release-package.mjs +45 -0
- package/scripts/write-release-metadata.mjs +152 -0
- package/scripts/write-release-metadata.test.mjs +37 -0
- package/src/BluetoothSdk.types.ts +1623 -0
- package/src/_internal.ts +16 -0
- package/src/_private/BluetoothSdkModule.ts +684 -0
- package/src/_private/LocalNetworkModule.ts +50 -0
- package/src/_private/cameraRequestPayload.ts +34 -0
- package/src/_private/photoRequestPayload.ts +90 -0
- package/src/_private/sdkOtaManifest.ts +18 -0
- package/src/changelogs.ts +46 -0
- package/src/debug.ts +9 -0
- package/src/generated/changelogCatalog.ts +13 -0
- package/src/generated/releaseMetadata.ts +20 -0
- package/src/index.ts +321 -0
- package/src/ota-server/OtaServer.types.ts +10 -0
- package/src/ota-server/OtaServerModule.ts +23 -0
- package/src/ota-server/OtaServerModule.web.ts +17 -0
- package/src/ota-server/index.ts +2 -0
- package/src/ota-transport/index.ts +116 -0
- package/src/photo-receiver/PhotoReceiver.types.ts +20 -0
- package/src/photo-receiver/PhotoReceiverModule.ts +11 -0
- package/src/photo-receiver/PhotoReceiverModule.web.ts +17 -0
- package/src/photo-receiver/index.ts +2 -0
- package/src/react/index.ts +21 -0
- package/src/react/useBluetooth.ts +309 -0
- package/src/react/useBluetoothEvent.ts +35 -0
- package/src/react/useBluetoothScan.ts +173 -0
- package/src/react/useBluetoothStatus.ts +180 -0
- package/src/react/useGlassesConnection.ts +202 -0
- package/src/types/index.ts +12 -0
package/android/src/test/java/com/glassly/bluetoothsdk/sgcs/MentraLiveGattCharacteristicsTest.kt
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package com.glassly.bluetoothsdk.sgcs
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertFalse
|
|
4
|
+
import org.junit.Assert.assertTrue
|
|
5
|
+
import org.junit.Test
|
|
6
|
+
|
|
7
|
+
class MentraLiveGattCharacteristicsTest {
|
|
8
|
+
@Test
|
|
9
|
+
fun `accepts the core characteristic pair without requiring optional characteristics`() {
|
|
10
|
+
assertTrue(
|
|
11
|
+
hasRequiredMentraLiveCoreCharacteristics(
|
|
12
|
+
hasRxCharacteristic = true,
|
|
13
|
+
hasTxCharacteristic = true,
|
|
14
|
+
)
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Test
|
|
19
|
+
fun `rejects an incomplete core characteristic pair`() {
|
|
20
|
+
assertFalse(
|
|
21
|
+
hasRequiredMentraLiveCoreCharacteristics(
|
|
22
|
+
hasRxCharacteristic = false,
|
|
23
|
+
hasTxCharacteristic = true,
|
|
24
|
+
)
|
|
25
|
+
)
|
|
26
|
+
assertFalse(
|
|
27
|
+
hasRequiredMentraLiveCoreCharacteristics(
|
|
28
|
+
hasRxCharacteristic = true,
|
|
29
|
+
hasTxCharacteristic = false,
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
assertFalse(
|
|
33
|
+
hasRequiredMentraLiveCoreCharacteristics(
|
|
34
|
+
hasRxCharacteristic = false,
|
|
35
|
+
hasTxCharacteristic = false,
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
package com.glassly.bluetoothsdk.sgcs
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.assertEquals
|
|
4
|
+
import org.junit.Assert.assertFalse
|
|
5
|
+
import org.junit.Assert.assertNull
|
|
6
|
+
import org.junit.Assert.assertTrue
|
|
7
|
+
import org.junit.Test
|
|
8
|
+
|
|
9
|
+
class MentraLivePairingAdvertisementParserTest {
|
|
10
|
+
@Test
|
|
11
|
+
fun connectedPairingRecoveryRequiresExplicitPendingTarget() {
|
|
12
|
+
assertFalse(
|
|
13
|
+
isPendingMentraLivePairingTarget(
|
|
14
|
+
connectedName = "MENTRA_LIVE_BLE_OWNER",
|
|
15
|
+
connectedAddress = "AA:BB:CC:DD:EE:FF",
|
|
16
|
+
pendingName = "",
|
|
17
|
+
pendingAddress = "",
|
|
18
|
+
)
|
|
19
|
+
)
|
|
20
|
+
assertTrue(
|
|
21
|
+
isPendingMentraLivePairingTarget(
|
|
22
|
+
connectedName = "MENTRA_LIVE_BLE_TARGET",
|
|
23
|
+
connectedAddress = "AA:BB:CC:DD:EE:FF",
|
|
24
|
+
pendingName = "MENTRA_LIVE_BLE_TARGET",
|
|
25
|
+
pendingAddress = "",
|
|
26
|
+
)
|
|
27
|
+
)
|
|
28
|
+
assertTrue(
|
|
29
|
+
isPendingMentraLivePairingTarget(
|
|
30
|
+
connectedName = "MENTRA_LIVE_BLE_TARGET",
|
|
31
|
+
connectedAddress = "AA:BB:CC:DD:EE:FF",
|
|
32
|
+
pendingName = "OTHER",
|
|
33
|
+
pendingAddress = "aa:bb:cc:dd:ee:ff",
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
assertFalse(
|
|
37
|
+
isPendingMentraLivePairingTarget(
|
|
38
|
+
connectedName = "MENTRA_LIVE_BLE_TARGET",
|
|
39
|
+
connectedAddress = "AA:BB:CC:DD:EE:FF",
|
|
40
|
+
pendingName = "MENTRA_LIVE_BLE_TARGET",
|
|
41
|
+
pendingAddress = "11:22:33:44:55:66",
|
|
42
|
+
)
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@Test
|
|
47
|
+
fun connectedPairingRecoveryRequiresActiveGattConnection() {
|
|
48
|
+
assertFalse(
|
|
49
|
+
shouldRecoverConnectedMentraLivePairingTarget(
|
|
50
|
+
isConnected = false,
|
|
51
|
+
connectedName = "MENTRA_LIVE_BLE_TARGET",
|
|
52
|
+
connectedAddress = "AA:BB:CC:DD:EE:FF",
|
|
53
|
+
pendingName = "MENTRA_LIVE_BLE_TARGET",
|
|
54
|
+
pendingAddress = "AA:BB:CC:DD:EE:FF",
|
|
55
|
+
)
|
|
56
|
+
)
|
|
57
|
+
assertTrue(
|
|
58
|
+
shouldRecoverConnectedMentraLivePairingTarget(
|
|
59
|
+
isConnected = true,
|
|
60
|
+
connectedName = "MENTRA_LIVE_BLE_TARGET",
|
|
61
|
+
connectedAddress = "AA:BB:CC:DD:EE:FF",
|
|
62
|
+
pendingName = "MENTRA_LIVE_BLE_TARGET",
|
|
63
|
+
pendingAddress = "AA:BB:CC:DD:EE:FF",
|
|
64
|
+
)
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@Test
|
|
69
|
+
fun parsesMarkedSecurePairingAdvertisement() {
|
|
70
|
+
val result = MentraLivePairingAdvertisementParser.parse(securePayload(pairingFlag = 1))
|
|
71
|
+
|
|
72
|
+
assertEquals("1234", result?.pairingCode)
|
|
73
|
+
assertTrue(result?.pairingMode == true)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Test
|
|
77
|
+
fun parsesMarkedOwnedAdvertisement() {
|
|
78
|
+
val result = MentraLivePairingAdvertisementParser.parse(securePayload(pairingFlag = 0))
|
|
79
|
+
|
|
80
|
+
assertEquals("1234", result?.pairingCode)
|
|
81
|
+
assertFalse(result?.pairingMode == true)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@Test
|
|
85
|
+
fun rejectsLegacyPayloadThatMatchesOldVersionCapabilityHeuristic() {
|
|
86
|
+
val legacyPayload = ByteArray(27)
|
|
87
|
+
legacyPayload[5] = 0
|
|
88
|
+
legacyPayload[6] = 1
|
|
89
|
+
legacyPayload[7] = 1
|
|
90
|
+
legacyPayload[8] = 0x34
|
|
91
|
+
legacyPayload[9] = 0x12
|
|
92
|
+
|
|
93
|
+
assertNull(MentraLivePairingAdvertisementParser.parse(legacyPayload))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@Test
|
|
97
|
+
fun rejectsUnmarkedFirstGenerationSecureTrailer() {
|
|
98
|
+
val unmarkedPayload = securePayload(pairingFlag = 1)
|
|
99
|
+
unmarkedPayload[10] = 0x11
|
|
100
|
+
unmarkedPayload[11] = 0x22
|
|
101
|
+
|
|
102
|
+
assertNull(MentraLivePairingAdvertisementParser.parse(unmarkedPayload))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@Test
|
|
106
|
+
fun rejectsEveryLegacyVersionAndCapabilityCombination() {
|
|
107
|
+
for (version in 0..255) {
|
|
108
|
+
for (capability in 0..255) {
|
|
109
|
+
val legacyPayload = ByteArray(27)
|
|
110
|
+
legacyPayload[5] = 1
|
|
111
|
+
legacyPayload[6] = version.toByte()
|
|
112
|
+
legacyPayload[7] = capability.toByte()
|
|
113
|
+
legacyPayload[10] = 0x4D
|
|
114
|
+
// Offset 11 is padding in the legacy format, so it cannot contain the second
|
|
115
|
+
// non-zero marker byte.
|
|
116
|
+
legacyPayload[11] = 0
|
|
117
|
+
assertNull(MentraLivePairingAdvertisementParser.parse(legacyPayload))
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private fun securePayload(pairingFlag: Int): ByteArray {
|
|
123
|
+
return ByteArray(27).also {
|
|
124
|
+
it[5] = pairingFlag.toByte()
|
|
125
|
+
it[6] = 2
|
|
126
|
+
it[7] = 1
|
|
127
|
+
it[8] = 0x34
|
|
128
|
+
it[9] = 0x12
|
|
129
|
+
it[10] = 0x4D
|
|
130
|
+
it[11] = 0x50
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
package com.glassly.bluetoothsdk.sgcs
|
|
2
|
+
|
|
3
|
+
import org.assertj.core.api.Assertions.assertThat
|
|
4
|
+
import org.junit.Test
|
|
5
|
+
|
|
6
|
+
class NimoCrc16Test {
|
|
7
|
+
|
|
8
|
+
@Test
|
|
9
|
+
fun `matches CRC-16-CCITT-FALSE check value`() {
|
|
10
|
+
// The standard check input "123456789" → 0x29B1 for CRC-16/CCITT-FALSE.
|
|
11
|
+
val data = "123456789".toByteArray(Charsets.US_ASCII)
|
|
12
|
+
assertThat(nimoCrc16(data)).isEqualTo(0x29B1)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@Test
|
|
16
|
+
fun `empty input yields init value`() {
|
|
17
|
+
assertThat(nimoCrc16(ByteArray(0))).isEqualTo(0xFFFF)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
class NimoFrameCodecTest {
|
|
22
|
+
|
|
23
|
+
@Test
|
|
24
|
+
fun `transport header layout`() {
|
|
25
|
+
val payload = byteArrayOf(0x03, 0x01, 0x01, 0x00, 0x42)
|
|
26
|
+
val header = NimoFrameCodec.transportHeader(payload, index = 0x0201, needsAck = true)
|
|
27
|
+
assertThat(header).hasSize(8)
|
|
28
|
+
assertThat(header[0].toInt() and 0xFF).isEqualTo(0xBF)
|
|
29
|
+
assertThat(header[1].toInt() and 0xFF).isEqualTo(0x02) // ack bit
|
|
30
|
+
assertThat(header[2].toInt() and 0xFF).isEqualTo(payload.size) // len LE
|
|
31
|
+
assertThat(header[3].toInt() and 0xFF).isEqualTo(0)
|
|
32
|
+
val crc = nimoCrc16(payload)
|
|
33
|
+
assertThat(header[4].toInt() and 0xFF).isEqualTo(crc and 0xFF)
|
|
34
|
+
assertThat(header[5].toInt() and 0xFF).isEqualTo((crc shr 8) and 0xFF)
|
|
35
|
+
assertThat(header[6].toInt() and 0xFF).isEqualTo(0x01) // index LE
|
|
36
|
+
assertThat(header[7].toInt() and 0xFF).isEqualTo(0x02)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@Test
|
|
40
|
+
fun `request frame layout`() {
|
|
41
|
+
// setBrightness level 8: cmd=0x03 key=0x02 payload=[0x08]
|
|
42
|
+
val frame = NimoFrameCodec.encodeFrame(0x03, 0x02, byteArrayOf(0x08))
|
|
43
|
+
assertThat(frame).hasSize(8 + 4 + 1)
|
|
44
|
+
// app header after transport header
|
|
45
|
+
assertThat(frame[8].toInt()).isEqualTo(0x03)
|
|
46
|
+
assertThat(frame[9].toInt()).isEqualTo(0x02)
|
|
47
|
+
assertThat(frame[10].toInt()).isEqualTo(0x01) // payload len LE
|
|
48
|
+
assertThat(frame[11].toInt()).isEqualTo(0x00)
|
|
49
|
+
assertThat(frame[12].toInt()).isEqualTo(0x08)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@Test
|
|
53
|
+
fun `decode parses a response frame and validates crc`() {
|
|
54
|
+
// Response layout: [cmd][key][len(2)][status][data]
|
|
55
|
+
val appPayload = byteArrayOf(0x02, 0x06, 0x04, 0x00, 0x00, 90, 85, 0, 1)
|
|
56
|
+
val frame = NimoFrameCodec.transportHeader(appPayload, index = 0) + appPayload
|
|
57
|
+
val decoded = NimoFrameCodec.decode(frame)
|
|
58
|
+
assertThat(decoded).isNotNull
|
|
59
|
+
assertThat(decoded!!.cmd).isEqualTo(0x02)
|
|
60
|
+
assertThat(decoded.key).isEqualTo(0x06)
|
|
61
|
+
assertThat(decoded.statusCode).isEqualTo(0)
|
|
62
|
+
assertThat(decoded.data).containsExactly(90, 85, 0, 1)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Test
|
|
66
|
+
fun `decode rejects corrupt crc`() {
|
|
67
|
+
val appPayload = byteArrayOf(0x02, 0x06, 0x04, 0x00, 0x00, 90, 85, 0, 1)
|
|
68
|
+
val frame = NimoFrameCodec.transportHeader(appPayload) + appPayload
|
|
69
|
+
frame[10] = (frame[10] + 1).toByte() // corrupt a payload byte after CRC was computed
|
|
70
|
+
assertThat(NimoFrameCodec.decode(frame)).isNull()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@Test
|
|
74
|
+
fun `decode rejects wrong magic and error status`() {
|
|
75
|
+
val appPayload = byteArrayOf(0x02, 0x06, 0x00, 0x00, 0x00)
|
|
76
|
+
val good = NimoFrameCodec.transportHeader(appPayload) + appPayload
|
|
77
|
+
val badMagic = good.copyOf().also { it[0] = 0x00 }
|
|
78
|
+
assertThat(NimoFrameCodec.decode(badMagic)).isNull()
|
|
79
|
+
val errStatus = good.copyOf().also { it[1] = 0x01 } // err bit set
|
|
80
|
+
assertThat(NimoFrameCodec.decode(errStatus)).isNull()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@Test
|
|
84
|
+
fun `single chunk content update has index zero`() {
|
|
85
|
+
val frames = NimoFrameCodec.updateContentFrames(0x06, 0, 0, 0x00, "hello".toByteArray())
|
|
86
|
+
assertThat(frames).hasSize(1)
|
|
87
|
+
val frame = frames[0]
|
|
88
|
+
// index bytes
|
|
89
|
+
assertThat(frame[6].toInt()).isEqualTo(0)
|
|
90
|
+
assertThat(frame[7].toInt()).isEqualTo(0)
|
|
91
|
+
// app header: cmd=0x07 key=0x04 len = 4 + 5
|
|
92
|
+
assertThat(frame[8].toInt()).isEqualTo(0x07)
|
|
93
|
+
assertThat(frame[9].toInt()).isEqualTo(0x04)
|
|
94
|
+
assertThat(frame[10].toInt()).isEqualTo(9)
|
|
95
|
+
// resource prefix [appId][layoutId][resId][resType]
|
|
96
|
+
assertThat(frame[12].toInt()).isEqualTo(0x06)
|
|
97
|
+
assertThat(frame[13].toInt()).isEqualTo(0x00)
|
|
98
|
+
assertThat(frame[14].toInt()).isEqualTo(0x00)
|
|
99
|
+
assertThat(frame[15].toInt()).isEqualTo(0x00)
|
|
100
|
+
assertThat(String(frame.copyOfRange(16, 21))).isEqualTo("hello")
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@Test
|
|
104
|
+
fun `multi chunk content update follows the index rule`() {
|
|
105
|
+
// Full app data = 4 (header) + 4 (prefix) + content. Pick content so we get 3 chunks:
|
|
106
|
+
// total = 8 + 1200 = 1208 → chunks of 501, 501, 206.
|
|
107
|
+
val content = ByteArray(1200) { (it % 251).toByte() }
|
|
108
|
+
val frames = NimoFrameCodec.updateContentFrames(0x06, 0, 0, 0x00, content)
|
|
109
|
+
assertThat(frames).hasSize(3)
|
|
110
|
+
// indices: 1, 2, 0 (last)
|
|
111
|
+
assertThat(frames[0][6].toInt()).isEqualTo(1)
|
|
112
|
+
assertThat(frames[1][6].toInt()).isEqualTo(2)
|
|
113
|
+
assertThat(frames[2][6].toInt()).isEqualTo(0)
|
|
114
|
+
// chunk sizes via the transport length field
|
|
115
|
+
fun len(f: ByteArray) = (f[2].toInt() and 0xFF) or ((f[3].toInt() and 0xFF) shl 8)
|
|
116
|
+
assertThat(len(frames[0])).isEqualTo(501)
|
|
117
|
+
assertThat(len(frames[1])).isEqualTo(501)
|
|
118
|
+
assertThat(len(frames[2])).isEqualTo(206)
|
|
119
|
+
// every chunk's CRC covers that chunk only
|
|
120
|
+
for (f in frames) {
|
|
121
|
+
val payload = f.copyOfRange(8, 8 + len(f))
|
|
122
|
+
val crc = (f[4].toInt() and 0xFF) or ((f[5].toInt() and 0xFF) shl 8)
|
|
123
|
+
assertThat(nimoCrc16(payload)).isEqualTo(crc)
|
|
124
|
+
}
|
|
125
|
+
// chunks concatenate back to the full app data
|
|
126
|
+
val merged = frames.flatMap { f -> f.copyOfRange(8, 8 + len(f)).toList() }.toByteArray()
|
|
127
|
+
assertThat(merged).hasSize(1208)
|
|
128
|
+
assertThat(merged[0].toInt()).isEqualTo(0x07)
|
|
129
|
+
assertThat(merged.copyOfRange(8, 1208)).isEqualTo(content)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@Test
|
|
133
|
+
fun `image header layout`() {
|
|
134
|
+
val header =
|
|
135
|
+
NimoFrameCodec.imageHeader(
|
|
136
|
+
width = 304,
|
|
137
|
+
height = 180,
|
|
138
|
+
formatBpp = 0x02,
|
|
139
|
+
compression = 0x07,
|
|
140
|
+
originalSize = 13680,
|
|
141
|
+
compressedSize = 4096
|
|
142
|
+
)
|
|
143
|
+
assertThat(header).hasSize(15)
|
|
144
|
+
assertThat(header[0].toInt() and 0xFF).isEqualTo(0x16)
|
|
145
|
+
assertThat((header[1].toInt() and 0xFF) or ((header[2].toInt() and 0xFF) shl 8)).isEqualTo(304)
|
|
146
|
+
assertThat((header[3].toInt() and 0xFF) or ((header[4].toInt() and 0xFF) shl 8)).isEqualTo(180)
|
|
147
|
+
assertThat(header[5].toInt()).isEqualTo(0x02)
|
|
148
|
+
assertThat(header[6].toInt()).isEqualTo(0x07)
|
|
149
|
+
val originalSize =
|
|
150
|
+
(header[7].toInt() and 0xFF) or
|
|
151
|
+
((header[8].toInt() and 0xFF) shl 8) or
|
|
152
|
+
((header[9].toInt() and 0xFF) shl 16) or
|
|
153
|
+
((header[10].toInt() and 0xFF) shl 24)
|
|
154
|
+
assertThat(originalSize).isEqualTo(13680)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
@Test
|
|
158
|
+
fun `device time is nine bytes with sane fields`() {
|
|
159
|
+
// 2026-06-11 is a Thursday (week=4 with Sunday=0).
|
|
160
|
+
val cal = java.util.Calendar.getInstance()
|
|
161
|
+
cal.set(2026, java.util.Calendar.JUNE, 11, 15, 30, 45)
|
|
162
|
+
val b = NimoFrameCodec.encodeDeviceTime(cal.timeInMillis)
|
|
163
|
+
assertThat(b).hasSize(9)
|
|
164
|
+
assertThat((b[0].toInt() and 0xFF) or ((b[1].toInt() and 0xFF) shl 8)).isEqualTo(2026)
|
|
165
|
+
assertThat(b[2].toInt()).isEqualTo(6)
|
|
166
|
+
assertThat(b[3].toInt()).isEqualTo(11)
|
|
167
|
+
assertThat(b[4].toInt()).isEqualTo(15)
|
|
168
|
+
assertThat(b[5].toInt()).isEqualTo(30)
|
|
169
|
+
assertThat(b[6].toInt()).isEqualTo(45)
|
|
170
|
+
assertThat(b[7].toInt()).isEqualTo(4)
|
|
171
|
+
// zone is the local offset in 15-minute units, clamped to ±48
|
|
172
|
+
val zone = b[8].toInt().toByte().toInt()
|
|
173
|
+
assertThat(zone).isBetween(-48, 48)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
class NimoReceiveAssemblerTest {
|
|
178
|
+
|
|
179
|
+
/** Builds one response fragment: transport header(index) + [cmd][key][len][status][data]. */
|
|
180
|
+
private fun fragment(cmd: Int, key: Int, status: Int, data: ByteArray, index: Int): ByteArray {
|
|
181
|
+
val appPayload =
|
|
182
|
+
byteArrayOf(
|
|
183
|
+
cmd.toByte(),
|
|
184
|
+
key.toByte(),
|
|
185
|
+
(data.size and 0xFF).toByte(),
|
|
186
|
+
((data.size shr 8) and 0xFF).toByte(),
|
|
187
|
+
status.toByte()
|
|
188
|
+
) + data
|
|
189
|
+
return NimoFrameCodec.transportHeader(appPayload, index = index) + appPayload
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
@Test
|
|
193
|
+
fun `single packet passes through unchanged`() {
|
|
194
|
+
val assembler = NimoReceiveAssembler()
|
|
195
|
+
val frame = fragment(0x02, 0x06, 0x00, byteArrayOf(90, 85, 0, 1), index = 0)
|
|
196
|
+
val out = assembler.ingest(frame)
|
|
197
|
+
assertThat(out).hasSize(1)
|
|
198
|
+
assertThat(out[0]).isEqualTo(frame)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@Test
|
|
202
|
+
fun `fragments merge in index order and reframe decodes`() {
|
|
203
|
+
val assembler = NimoReceiveAssembler()
|
|
204
|
+
assertThat(assembler.ingest(fragment(0x02, 0x0B, 0x00, "ver".toByteArray(), index = 1))).isEmpty()
|
|
205
|
+
assertThat(assembler.ingest(fragment(0x02, 0x0B, 0x00, "sion-".toByteArray(), index = 2))).isEmpty()
|
|
206
|
+
val out = assembler.ingest(fragment(0x02, 0x0B, 0x00, "1.2.3".toByteArray(), index = 0))
|
|
207
|
+
assertThat(out).hasSize(1)
|
|
208
|
+
val decoded = NimoFrameCodec.decode(out[0])
|
|
209
|
+
assertThat(decoded).isNotNull
|
|
210
|
+
assertThat(decoded!!.cmd).isEqualTo(0x02)
|
|
211
|
+
assertThat(decoded.key).isEqualTo(0x0B)
|
|
212
|
+
assertThat(decoded.statusCode).isEqualTo(0)
|
|
213
|
+
assertThat(String(decoded.data!!)).isEqualTo("version-1.2.3")
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@Test
|
|
217
|
+
fun `out of order middle fragment is tolerated`() {
|
|
218
|
+
val assembler = NimoReceiveAssembler()
|
|
219
|
+
assertThat(assembler.ingest(fragment(0x02, 0x0B, 0x00, "B".toByteArray(), index = 2))).isEmpty()
|
|
220
|
+
assertThat(assembler.ingest(fragment(0x02, 0x0B, 0x00, "A".toByteArray(), index = 1))).isEmpty()
|
|
221
|
+
val out = assembler.ingest(fragment(0x02, 0x0B, 0x00, "C".toByteArray(), index = 0))
|
|
222
|
+
assertThat(out).hasSize(1)
|
|
223
|
+
val decoded = NimoFrameCodec.decode(out[0])
|
|
224
|
+
assertThat(String(decoded!!.data!!)).isEqualTo("ABC")
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
@Test
|
|
228
|
+
fun `stale first fragment is dropped when a new round starts`() {
|
|
229
|
+
val assembler = NimoReceiveAssembler()
|
|
230
|
+
// Round 1 loses its last packet.
|
|
231
|
+
assertThat(assembler.ingest(fragment(0x02, 0x0B, 0x00, "stale".toByteArray(), index = 1))).isEmpty()
|
|
232
|
+
// Round 2 starts fresh.
|
|
233
|
+
assertThat(assembler.ingest(fragment(0x02, 0x0B, 0x00, "fresh".toByteArray(), index = 1))).isEmpty()
|
|
234
|
+
val out = assembler.ingest(fragment(0x02, 0x0B, 0x00, "!".toByteArray(), index = 0))
|
|
235
|
+
val decoded = NimoFrameCodec.decode(out[0])
|
|
236
|
+
assertThat(String(decoded!!.data!!)).isEqualTo("fresh!")
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@Test
|
|
240
|
+
fun `different cmd-key groups do not cross-merge`() {
|
|
241
|
+
val assembler = NimoReceiveAssembler()
|
|
242
|
+
assertThat(assembler.ingest(fragment(0x02, 0x0B, 0x00, "v".toByteArray(), index = 1))).isEmpty()
|
|
243
|
+
// A complete single frame from another (cmd,key) passes through while the group is open.
|
|
244
|
+
val battery = fragment(0x02, 0x06, 0x00, byteArrayOf(90, 85, 0, 1), index = 0)
|
|
245
|
+
assertThat(assembler.ingest(battery)).containsExactly(battery)
|
|
246
|
+
val out = assembler.ingest(fragment(0x02, 0x0B, 0x00, "1".toByteArray(), index = 0))
|
|
247
|
+
assertThat(String(NimoFrameCodec.decode(out[0])!!.data!!)).isEqualTo("v1")
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
class NimoAudioParserTest {
|
|
252
|
+
|
|
253
|
+
private fun audioPacket(type: Int, sn: Int, frames: List<ByteArray>): ByteArray {
|
|
254
|
+
val payload = ByteArrayPacker()
|
|
255
|
+
payload.add((sn and 0xFF).toByte(), ((sn shr 8) and 0xFF).toByte())
|
|
256
|
+
payload.add(frames.size.toByte(), 0x00)
|
|
257
|
+
for (opus in frames) {
|
|
258
|
+
val body = ByteArray(8 + opus.size)
|
|
259
|
+
body[3] = opus.size.toByte() // opusLen at body[3]
|
|
260
|
+
opus.copyInto(body, 8)
|
|
261
|
+
payload.add((body.size).toByte())
|
|
262
|
+
payload.addAll(body)
|
|
263
|
+
}
|
|
264
|
+
val p = payload.toByteArray()
|
|
265
|
+
return byteArrayOf(0x52, type.toByte(), (p.size and 0xFF).toByte(), ((p.size shr 8) and 0xFF).toByte()) + p
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private class ByteArrayPacker {
|
|
269
|
+
private val out = java.io.ByteArrayOutputStream()
|
|
270
|
+
fun add(vararg bytes: Byte) = out.write(bytes)
|
|
271
|
+
fun addAll(bytes: ByteArray) = out.write(bytes)
|
|
272
|
+
fun toByteArray(): ByteArray = out.toByteArray()
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@Test
|
|
276
|
+
fun `parses right-ear opus frames sliced by opusLen`() {
|
|
277
|
+
val opus1 = byteArrayOf(0xAA.toByte(), 0xBB.toByte(), 0xCC.toByte())
|
|
278
|
+
val opus2 = byteArrayOf(0x01, 0x02)
|
|
279
|
+
val packet = audioPacket(NimoAudioParser.TYPE_OPUS_RIGHT, 0x1234, listOf(opus1, opus2))
|
|
280
|
+
val parsed = NimoAudioParser.parse(packet)
|
|
281
|
+
assertThat(parsed).isNotNull
|
|
282
|
+
assertThat(parsed!!.type).isEqualTo(NimoAudioParser.TYPE_OPUS_RIGHT)
|
|
283
|
+
assertThat(parsed.sequence).isEqualTo(0x1234)
|
|
284
|
+
assertThat(parsed.opusFrames).hasSize(2)
|
|
285
|
+
assertThat(parsed.opusFrames[0]).isEqualTo(opus1)
|
|
286
|
+
assertThat(parsed.opusFrames[1]).isEqualTo(opus2)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
@Test
|
|
290
|
+
fun `start and stop packets carry no frames`() {
|
|
291
|
+
assertThat(NimoAudioParser.parse(byteArrayOf(0x52, 0x01, 0x00, 0x00))!!.type)
|
|
292
|
+
.isEqualTo(NimoAudioParser.TYPE_START)
|
|
293
|
+
assertThat(NimoAudioParser.parse(byteArrayOf(0x52, 0x00, 0x00, 0x00))!!.type)
|
|
294
|
+
.isEqualTo(NimoAudioParser.TYPE_STOP)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
@Test
|
|
298
|
+
fun `rejects wrong header and short input`() {
|
|
299
|
+
assertThat(NimoAudioParser.parse(byteArrayOf(0x51, 0x02, 0x00, 0x00))).isNull()
|
|
300
|
+
assertThat(NimoAudioParser.parse(byteArrayOf(0x52, 0x02))).isNull()
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
@Test
|
|
304
|
+
fun `skips frames with short header or opus overrun`() {
|
|
305
|
+
// frame 1: frameLen=4 (<8) → skipped; frame 2: opusLen overruns frameLen → skipped;
|
|
306
|
+
// frame 3: valid.
|
|
307
|
+
val valid = byteArrayOf(0x0F)
|
|
308
|
+
val payload = ByteArrayPacker()
|
|
309
|
+
payload.add(0x01, 0x00) // SN
|
|
310
|
+
payload.add(0x03, 0x00) // frameCnt=3
|
|
311
|
+
// frame 1: short header
|
|
312
|
+
payload.add(0x04)
|
|
313
|
+
payload.addAll(ByteArray(4))
|
|
314
|
+
// frame 2: opusLen 20 > frameLen-8
|
|
315
|
+
val bad = ByteArray(10)
|
|
316
|
+
bad[3] = 20
|
|
317
|
+
payload.add(0x0A)
|
|
318
|
+
payload.addAll(bad)
|
|
319
|
+
// frame 3: valid 1-byte opus
|
|
320
|
+
val body = ByteArray(9)
|
|
321
|
+
body[3] = 1
|
|
322
|
+
body[8] = valid[0]
|
|
323
|
+
payload.add(0x09)
|
|
324
|
+
payload.addAll(body)
|
|
325
|
+
val p = payload.toByteArray()
|
|
326
|
+
val packet =
|
|
327
|
+
byteArrayOf(0x52, 0x02, (p.size and 0xFF).toByte(), ((p.size shr 8) and 0xFF).toByte()) + p
|
|
328
|
+
|
|
329
|
+
val parsed = NimoAudioParser.parse(packet)
|
|
330
|
+
assertThat(parsed!!.opusFrames).hasSize(1)
|
|
331
|
+
assertThat(parsed.opusFrames[0]).isEqualTo(valid)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
package com.glassly.bluetoothsdk.utils;
|
|
2
|
+
|
|
3
|
+
import static org.assertj.core.api.Assertions.assertThat;
|
|
4
|
+
|
|
5
|
+
import org.junit.Test;
|
|
6
|
+
import org.junit.runner.RunWith;
|
|
7
|
+
import org.robolectric.RobolectricTestRunner;
|
|
8
|
+
import org.robolectric.annotation.Config;
|
|
9
|
+
|
|
10
|
+
import java.io.File;
|
|
11
|
+
|
|
12
|
+
@RunWith(RobolectricTestRunner.class)
|
|
13
|
+
@Config(sdk = 31)
|
|
14
|
+
public class AvifExifStripperTest {
|
|
15
|
+
|
|
16
|
+
@Test
|
|
17
|
+
public void stripForDecode_removesExifTailAndKeepsPrimaryIlocValid() throws Exception {
|
|
18
|
+
byte[] withExif =
|
|
19
|
+
java.nio.file.Files.readAllBytes(
|
|
20
|
+
new File("src/test/resources/avif_with_exif.avif").toPath());
|
|
21
|
+
assertThat(BlePhotoUploadService.containsExifMarkerInBytes(withExif)).isTrue();
|
|
22
|
+
int originalMarkerCount = countExifMarkers(withExif);
|
|
23
|
+
|
|
24
|
+
byte[] stripped = AvifExifStripper.stripForDecode(withExif);
|
|
25
|
+
assertThat(stripped.length).isLessThan(withExif.length);
|
|
26
|
+
assertThat(countExifMarkers(stripped)).isLessThan(originalMarkerCount);
|
|
27
|
+
|
|
28
|
+
byte[] jpeg = BlePhotoUploadService.convertToJpegPreservingExif(withExif);
|
|
29
|
+
assertThat(jpeg.length).isGreaterThan(100);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private static int countExifMarkers(byte[] data) {
|
|
33
|
+
byte[] marker = new byte[] {'E', 'x', 'i', 'f', 0, 0};
|
|
34
|
+
int count = 0;
|
|
35
|
+
outer:
|
|
36
|
+
for (int i = 0; i <= data.length - marker.length; i++) {
|
|
37
|
+
for (int j = 0; j < marker.length; j++) {
|
|
38
|
+
if (data[i + j] != marker[j]) {
|
|
39
|
+
continue outer;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
count++;
|
|
43
|
+
}
|
|
44
|
+
return count;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
package com.glassly.bluetoothsdk.utils;
|
|
2
|
+
|
|
3
|
+
import static org.junit.Assert.assertEquals;
|
|
4
|
+
import static org.junit.Assert.assertFalse;
|
|
5
|
+
import static org.junit.Assert.assertNotNull;
|
|
6
|
+
import static org.junit.Assert.assertTrue;
|
|
7
|
+
|
|
8
|
+
import org.junit.Test;
|
|
9
|
+
import org.junit.runner.RunWith;
|
|
10
|
+
import org.robolectric.RobolectricTestRunner;
|
|
11
|
+
import org.robolectric.annotation.Config;
|
|
12
|
+
|
|
13
|
+
import java.nio.charset.StandardCharsets;
|
|
14
|
+
import java.util.List;
|
|
15
|
+
|
|
16
|
+
@RunWith(RobolectricTestRunner.class)
|
|
17
|
+
@Config(sdk = 33)
|
|
18
|
+
public class BinaryMessageChunkerTest {
|
|
19
|
+
|
|
20
|
+
@Test
|
|
21
|
+
public void smallPayloadProducesSingleFragment() {
|
|
22
|
+
byte[] payload = "{\"type\":\"ping\"}".getBytes(StandardCharsets.UTF_8);
|
|
23
|
+
|
|
24
|
+
assertFalse(MessageChunker.needsBinaryFragmenting(payload));
|
|
25
|
+
|
|
26
|
+
List<MessageChunker.BinaryFragment> fragments =
|
|
27
|
+
MessageChunker.createBinaryFragments(payload, 42, false, false);
|
|
28
|
+
|
|
29
|
+
assertEquals(1, fragments.size());
|
|
30
|
+
assertEquals(42, fragments.get(0).msgId);
|
|
31
|
+
assertEquals(0, fragments.get(0).fragIdx);
|
|
32
|
+
assertEquals(1, fragments.get(0).fragCount);
|
|
33
|
+
assertTrue(MessageChunker.allBinaryFragmentsFit(fragments));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Test
|
|
37
|
+
public void largePayloadSplitsIntoMultipleFragments() {
|
|
38
|
+
byte[] payload = new byte[1000];
|
|
39
|
+
for (int i = 0; i < payload.length; i++) {
|
|
40
|
+
payload[i] = (byte) ('a' + (i % 26));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
assertTrue(MessageChunker.needsBinaryFragmenting(payload));
|
|
44
|
+
|
|
45
|
+
List<MessageChunker.BinaryFragment> fragments =
|
|
46
|
+
MessageChunker.createBinaryFragments(payload, 7, true, true);
|
|
47
|
+
|
|
48
|
+
assertEquals(3, fragments.size());
|
|
49
|
+
assertEquals(7, fragments.get(0).msgId);
|
|
50
|
+
assertEquals(0, fragments.get(0).fragIdx);
|
|
51
|
+
assertEquals(2, fragments.get(2).fragIdx);
|
|
52
|
+
assertEquals(3, fragments.get(0).fragCount);
|
|
53
|
+
assertTrue((fragments.get(0).flags & BleWireProtocol.BLE_WIRE_FLAG_WAKE) != 0);
|
|
54
|
+
assertTrue((fragments.get(2).flags & BleWireProtocol.BLE_WIRE_FLAG_ACK_REQUESTED) != 0);
|
|
55
|
+
assertTrue(MessageChunker.allBinaryFragmentsFit(fragments));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Test
|
|
59
|
+
public void binaryReassemblerRebuildsPayload() {
|
|
60
|
+
byte[] payload = new byte[900];
|
|
61
|
+
for (int i = 0; i < payload.length; i++) {
|
|
62
|
+
payload[i] = (byte) (i & 0xFF);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
List<MessageChunker.BinaryFragment> fragments =
|
|
66
|
+
MessageChunker.createBinaryFragments(payload, 99, false, false);
|
|
67
|
+
MessageChunkReassembler reassembler = new MessageChunkReassembler();
|
|
68
|
+
|
|
69
|
+
byte[] rebuilt = null;
|
|
70
|
+
for (MessageChunker.BinaryFragment fragment : fragments) {
|
|
71
|
+
rebuilt = reassembler.addBinaryFragment(
|
|
72
|
+
fragment.msgId,
|
|
73
|
+
fragment.fragIdx,
|
|
74
|
+
fragment.fragCount,
|
|
75
|
+
fragment.payload);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
assertNotNull(rebuilt);
|
|
79
|
+
assertEquals(payload.length, rebuilt.length);
|
|
80
|
+
for (int i = 0; i < payload.length; i++) {
|
|
81
|
+
assertEquals(payload[i], rebuilt[i]);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|