@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
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
package com.glassly.bluetoothsdk
|
|
2
|
+
|
|
3
|
+
import android.net.ConnectivityManager
|
|
4
|
+
import android.net.Network
|
|
5
|
+
import android.net.NetworkCapabilities
|
|
6
|
+
import android.net.NetworkRequest
|
|
7
|
+
import android.net.wifi.WifiNetworkSpecifier
|
|
8
|
+
import android.os.Handler
|
|
9
|
+
import android.os.Looper
|
|
10
|
+
import android.util.Base64
|
|
11
|
+
import android.util.Log
|
|
12
|
+
import expo.modules.kotlin.Promise
|
|
13
|
+
import expo.modules.kotlin.modules.Module
|
|
14
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
15
|
+
import java.io.File
|
|
16
|
+
import java.io.FileOutputStream
|
|
17
|
+
import java.net.HttpURLConnection
|
|
18
|
+
import java.net.Inet4Address
|
|
19
|
+
import java.net.URL
|
|
20
|
+
import java.util.concurrent.ConcurrentHashMap
|
|
21
|
+
import java.util.concurrent.Executors
|
|
22
|
+
|
|
23
|
+
/** Glassly-internal transport for glasses-local traffic without process network binding. */
|
|
24
|
+
class LocalNetworkModule : Module() {
|
|
25
|
+
private companion object {
|
|
26
|
+
const val TAG = "LocalNetwork"
|
|
27
|
+
const val NETWORK_READINESS_TIMEOUT_MS = 5_000L
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private val lock = Any()
|
|
31
|
+
private val executor = Executors.newCachedThreadPool()
|
|
32
|
+
private val handler = Handler(Looper.getMainLooper())
|
|
33
|
+
private val activeConnections = ConcurrentHashMap<String, HttpURLConnection>()
|
|
34
|
+
private val networkReadiness = ScopedNetworkReadiness<Network>()
|
|
35
|
+
private var localNetwork: Network? = null
|
|
36
|
+
private var networkCallback: ConnectivityManager.NetworkCallback? = null
|
|
37
|
+
private var pendingConnectPromise: Promise? = null
|
|
38
|
+
private var pendingReadinessTimeout: Runnable? = null
|
|
39
|
+
|
|
40
|
+
override fun definition() = ModuleDefinition {
|
|
41
|
+
Name("LocalNetwork")
|
|
42
|
+
Events("downloadProgress", "networkLost")
|
|
43
|
+
|
|
44
|
+
AsyncFunction("connect") { ssid: String, password: String, promise: Promise ->
|
|
45
|
+
connect(ssid, password, promise)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
AsyncFunction("request") {
|
|
49
|
+
requestId: String,
|
|
50
|
+
url: String,
|
|
51
|
+
method: String,
|
|
52
|
+
headers: Map<String, String>,
|
|
53
|
+
body: String?,
|
|
54
|
+
timeoutMs: Int,
|
|
55
|
+
promise: Promise ->
|
|
56
|
+
executor.execute { performRequest(requestId, url, method, headers, body, timeoutMs, promise) }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
AsyncFunction("download") {
|
|
60
|
+
requestId: String,
|
|
61
|
+
url: String,
|
|
62
|
+
destination: String,
|
|
63
|
+
headers: Map<String, String>,
|
|
64
|
+
connectionTimeoutMs: Int,
|
|
65
|
+
readTimeoutMs: Int,
|
|
66
|
+
promise: Promise ->
|
|
67
|
+
executor.execute {
|
|
68
|
+
performDownload(
|
|
69
|
+
requestId,
|
|
70
|
+
url,
|
|
71
|
+
destination,
|
|
72
|
+
headers,
|
|
73
|
+
connectionTimeoutMs,
|
|
74
|
+
readTimeoutMs,
|
|
75
|
+
promise,
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
AsyncFunction("cancel") { requestId: String -> cancel(requestId) }
|
|
81
|
+
AsyncFunction("disconnect") { disconnect() }
|
|
82
|
+
|
|
83
|
+
OnDestroy {
|
|
84
|
+
disconnect()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private fun connectivityManager(): ConnectivityManager =
|
|
89
|
+
requireNotNull(appContext.reactContext?.getSystemService(ConnectivityManager::class.java)) {
|
|
90
|
+
"ConnectivityManager is unavailable"
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private fun connect(ssid: String, password: String, promise: Promise) {
|
|
94
|
+
disconnect()
|
|
95
|
+
val manager = connectivityManager()
|
|
96
|
+
val specifier =
|
|
97
|
+
WifiNetworkSpecifier.Builder()
|
|
98
|
+
.setSsid(ssid)
|
|
99
|
+
.setWpa2Passphrase(password)
|
|
100
|
+
.build()
|
|
101
|
+
val request =
|
|
102
|
+
NetworkRequest.Builder()
|
|
103
|
+
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
|
104
|
+
.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
|
105
|
+
.setNetworkSpecifier(specifier)
|
|
106
|
+
.build()
|
|
107
|
+
|
|
108
|
+
val callback =
|
|
109
|
+
object : ConnectivityManager.NetworkCallback() {
|
|
110
|
+
override fun onAvailable(network: Network) {
|
|
111
|
+
synchronized(lock) {
|
|
112
|
+
if (networkCallback !== this) {
|
|
113
|
+
Log.d(TAG, "Ignoring onAvailable from a stale network request")
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
networkReadiness.onAvailable(network)
|
|
117
|
+
}
|
|
118
|
+
Log.i(
|
|
119
|
+
TAG,
|
|
120
|
+
"Captured glasses WiFi network=$network; waiting for foreground capability " +
|
|
121
|
+
"processBoundNetwork=${manager.boundNetworkForProcess}",
|
|
122
|
+
)
|
|
123
|
+
scheduleReadinessTimeout(this, ssid)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
override fun onCapabilitiesChanged(network: Network, capabilities: NetworkCapabilities) {
|
|
127
|
+
val connectPromise: Promise?
|
|
128
|
+
synchronized(lock) {
|
|
129
|
+
if (networkCallback !== this || localNetwork != null || pendingConnectPromise == null) return
|
|
130
|
+
if (!networkReadiness.isReady(
|
|
131
|
+
network,
|
|
132
|
+
capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND),
|
|
133
|
+
)
|
|
134
|
+
) {
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
localNetwork = network
|
|
138
|
+
connectPromise = pendingConnectPromise
|
|
139
|
+
pendingConnectPromise = null
|
|
140
|
+
cancelReadinessTimeoutLocked()
|
|
141
|
+
}
|
|
142
|
+
Log.i(
|
|
143
|
+
TAG,
|
|
144
|
+
"Glasses WiFi network=$network is foreground and ready " +
|
|
145
|
+
"processBoundNetwork=${manager.boundNetworkForProcess}",
|
|
146
|
+
)
|
|
147
|
+
val localAddress =
|
|
148
|
+
manager.getLinkProperties(network)
|
|
149
|
+
?.linkAddresses
|
|
150
|
+
?.firstOrNull { it.address is Inet4Address }
|
|
151
|
+
?.address
|
|
152
|
+
?.hostAddress
|
|
153
|
+
connectPromise?.resolve(
|
|
154
|
+
buildMap<String, Any> {
|
|
155
|
+
put("connected", true)
|
|
156
|
+
put("ssid", ssid)
|
|
157
|
+
if (!localAddress.isNullOrBlank()) put("localAddress", localAddress)
|
|
158
|
+
},
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
override fun onUnavailable() {
|
|
163
|
+
synchronized(lock) {
|
|
164
|
+
if (networkCallback !== this) {
|
|
165
|
+
Log.d(TAG, "Ignoring onUnavailable from a stale network request")
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
rejectPendingConnect("ERR_LOCAL_WIFI_UNAVAILABLE", "Could not connect to $ssid")
|
|
170
|
+
clearNetworkCallback(this)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
override fun onLost(network: Network) {
|
|
174
|
+
synchronized(lock) {
|
|
175
|
+
if (networkCallback !== this ||
|
|
176
|
+
(localNetwork != network && !networkReadiness.contains(network))
|
|
177
|
+
) {
|
|
178
|
+
Log.d(TAG, "Ignoring onLost from a stale network request")
|
|
179
|
+
return
|
|
180
|
+
}
|
|
181
|
+
localNetwork = null
|
|
182
|
+
networkReadiness.clear()
|
|
183
|
+
}
|
|
184
|
+
cancelAll()
|
|
185
|
+
rejectPendingConnect("ERR_LOCAL_WIFI_LOST", "Lost connection to $ssid")
|
|
186
|
+
clearNetworkCallback(this)
|
|
187
|
+
sendEvent("networkLost", mapOf("ssid" to ssid))
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
synchronized(lock) {
|
|
192
|
+
pendingConnectPromise = promise
|
|
193
|
+
networkCallback = callback
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
manager.requestNetwork(request, callback, 30_000)
|
|
197
|
+
} catch (error: Exception) {
|
|
198
|
+
clearNetworkCallback(callback)
|
|
199
|
+
rejectPendingConnect("ERR_LOCAL_WIFI_REQUEST", error.message ?: "Local WiFi request failed", error)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private fun performRequest(
|
|
204
|
+
requestId: String,
|
|
205
|
+
url: String,
|
|
206
|
+
method: String,
|
|
207
|
+
headers: Map<String, String>,
|
|
208
|
+
body: String?,
|
|
209
|
+
timeoutMs: Int,
|
|
210
|
+
promise: Promise,
|
|
211
|
+
) {
|
|
212
|
+
var connection: HttpURLConnection? = null
|
|
213
|
+
try {
|
|
214
|
+
connection = openConnection(requestId, url)
|
|
215
|
+
connection.requestMethod = method
|
|
216
|
+
connection.connectTimeout = timeoutMs
|
|
217
|
+
connection.readTimeout = timeoutMs
|
|
218
|
+
headers.forEach(connection::setRequestProperty)
|
|
219
|
+
if (body != null) {
|
|
220
|
+
connection.doOutput = true
|
|
221
|
+
connection.outputStream.use { it.write(body.toByteArray(Charsets.UTF_8)) }
|
|
222
|
+
}
|
|
223
|
+
val status = connection.responseCode
|
|
224
|
+
val stream = if (status >= 400) connection.errorStream else connection.inputStream
|
|
225
|
+
val bytes = stream?.use { it.readBytes() } ?: ByteArray(0)
|
|
226
|
+
promise.resolve(
|
|
227
|
+
mapOf(
|
|
228
|
+
"status" to status,
|
|
229
|
+
"headers" to responseHeaders(connection),
|
|
230
|
+
"bodyBase64" to Base64.encodeToString(bytes, Base64.NO_WRAP),
|
|
231
|
+
),
|
|
232
|
+
)
|
|
233
|
+
} catch (error: Exception) {
|
|
234
|
+
promise.reject("ERR_LOCAL_HTTP", error.message ?: "Local HTTP request failed", error)
|
|
235
|
+
} finally {
|
|
236
|
+
activeConnections.remove(requestId)
|
|
237
|
+
connection?.disconnect()
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private fun performDownload(
|
|
242
|
+
requestId: String,
|
|
243
|
+
url: String,
|
|
244
|
+
destination: String,
|
|
245
|
+
headers: Map<String, String>,
|
|
246
|
+
connectionTimeoutMs: Int,
|
|
247
|
+
readTimeoutMs: Int,
|
|
248
|
+
promise: Promise,
|
|
249
|
+
) {
|
|
250
|
+
var connection: HttpURLConnection? = null
|
|
251
|
+
try {
|
|
252
|
+
connection = openConnection(requestId, url)
|
|
253
|
+
connection.requestMethod = "GET"
|
|
254
|
+
connection.connectTimeout = connectionTimeoutMs
|
|
255
|
+
connection.readTimeout = readTimeoutMs
|
|
256
|
+
headers.forEach(connection::setRequestProperty)
|
|
257
|
+
val status = connection.responseCode
|
|
258
|
+
if (status !in 200..299) throw IllegalStateException("HTTP $status")
|
|
259
|
+
val contentLength = connection.contentLengthLong
|
|
260
|
+
val responseHeaders = responseHeaders(connection)
|
|
261
|
+
|
|
262
|
+
// Match RNFS's begin callback contract before the first response byte. Gallery
|
|
263
|
+
// resumable downloads need the real status and Content-Range to validate segments.
|
|
264
|
+
sendEvent(
|
|
265
|
+
"downloadProgress",
|
|
266
|
+
mapOf(
|
|
267
|
+
"requestId" to requestId,
|
|
268
|
+
"bytesWritten" to 0L,
|
|
269
|
+
"contentLength" to contentLength,
|
|
270
|
+
"statusCode" to status,
|
|
271
|
+
"headers" to responseHeaders,
|
|
272
|
+
),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
val destinationFile = File(destination)
|
|
276
|
+
destinationFile.parentFile?.mkdirs()
|
|
277
|
+
var bytesWritten = 0L
|
|
278
|
+
connection.inputStream.use { input ->
|
|
279
|
+
FileOutputStream(destinationFile, false).use { output ->
|
|
280
|
+
val buffer = ByteArray(64 * 1024)
|
|
281
|
+
while (true) {
|
|
282
|
+
val count = input.read(buffer)
|
|
283
|
+
if (count < 0) break
|
|
284
|
+
output.write(buffer, 0, count)
|
|
285
|
+
bytesWritten += count
|
|
286
|
+
sendEvent(
|
|
287
|
+
"downloadProgress",
|
|
288
|
+
mapOf(
|
|
289
|
+
"requestId" to requestId,
|
|
290
|
+
"bytesWritten" to bytesWritten,
|
|
291
|
+
"contentLength" to contentLength,
|
|
292
|
+
),
|
|
293
|
+
)
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
promise.resolve(
|
|
298
|
+
mapOf(
|
|
299
|
+
"statusCode" to status,
|
|
300
|
+
"bytesWritten" to bytesWritten,
|
|
301
|
+
"headers" to responseHeaders,
|
|
302
|
+
),
|
|
303
|
+
)
|
|
304
|
+
} catch (error: Exception) {
|
|
305
|
+
promise.reject("ERR_LOCAL_DOWNLOAD", error.message ?: "Local download failed", error)
|
|
306
|
+
} finally {
|
|
307
|
+
activeConnections.remove(requestId)
|
|
308
|
+
connection?.disconnect()
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private fun openConnection(requestId: String, url: String): HttpURLConnection {
|
|
313
|
+
val network = synchronized(lock) { localNetwork }
|
|
314
|
+
?: throw IllegalStateException("Local WiFi is not connected")
|
|
315
|
+
val connection = network.openConnection(URL(url)) as HttpURLConnection
|
|
316
|
+
activeConnections[requestId] = connection
|
|
317
|
+
return connection
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private fun responseHeaders(connection: HttpURLConnection): Map<String, String> =
|
|
321
|
+
connection.headerFields
|
|
322
|
+
.filterKeys { it != null }
|
|
323
|
+
.mapValues { (_, values) -> values.joinToString(", ") }
|
|
324
|
+
|
|
325
|
+
private fun cancel(requestId: String) {
|
|
326
|
+
activeConnections.remove(requestId)?.disconnect()
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private fun cancelAll() {
|
|
330
|
+
activeConnections.keys.toList().forEach(::cancel)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private fun scheduleReadinessTimeout(
|
|
334
|
+
callback: ConnectivityManager.NetworkCallback,
|
|
335
|
+
ssid: String,
|
|
336
|
+
) {
|
|
337
|
+
val timeout =
|
|
338
|
+
Runnable {
|
|
339
|
+
val connectPromise = synchronized(lock) {
|
|
340
|
+
if (networkCallback !== callback || pendingConnectPromise == null) return@Runnable
|
|
341
|
+
pendingReadinessTimeout = null
|
|
342
|
+
val promise = pendingConnectPromise
|
|
343
|
+
pendingConnectPromise = null
|
|
344
|
+
promise
|
|
345
|
+
}
|
|
346
|
+
connectPromise?.reject(
|
|
347
|
+
"ERR_LOCAL_WIFI_NOT_READY",
|
|
348
|
+
"Connected to $ssid but the network did not become ready",
|
|
349
|
+
null,
|
|
350
|
+
)
|
|
351
|
+
clearNetworkCallback(callback)
|
|
352
|
+
}
|
|
353
|
+
synchronized(lock) {
|
|
354
|
+
if (networkCallback !== callback || pendingConnectPromise == null) return
|
|
355
|
+
cancelReadinessTimeoutLocked()
|
|
356
|
+
pendingReadinessTimeout = timeout
|
|
357
|
+
}
|
|
358
|
+
handler.postDelayed(timeout, NETWORK_READINESS_TIMEOUT_MS)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private fun cancelReadinessTimeoutLocked() {
|
|
362
|
+
pendingReadinessTimeout?.let(handler::removeCallbacks)
|
|
363
|
+
pendingReadinessTimeout = null
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private fun disconnect() {
|
|
367
|
+
cancelAll()
|
|
368
|
+
val manager = appContext.reactContext?.getSystemService(ConnectivityManager::class.java)
|
|
369
|
+
val callback: ConnectivityManager.NetworkCallback?
|
|
370
|
+
synchronized(lock) {
|
|
371
|
+
callback = networkCallback
|
|
372
|
+
networkCallback = null
|
|
373
|
+
localNetwork = null
|
|
374
|
+
networkReadiness.clear()
|
|
375
|
+
cancelReadinessTimeoutLocked()
|
|
376
|
+
}
|
|
377
|
+
if (callback != null && manager != null) {
|
|
378
|
+
try {
|
|
379
|
+
manager.unregisterNetworkCallback(callback)
|
|
380
|
+
} catch (_: IllegalArgumentException) {
|
|
381
|
+
// Already unregistered by the platform.
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
rejectPendingConnect("ERR_LOCAL_WIFI_CANCELLED", "Local WiFi connection cancelled")
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
private fun clearNetworkCallback(callback: ConnectivityManager.NetworkCallback) {
|
|
388
|
+
val manager = appContext.reactContext?.getSystemService(ConnectivityManager::class.java)
|
|
389
|
+
synchronized(lock) {
|
|
390
|
+
if (networkCallback == callback) {
|
|
391
|
+
networkCallback = null
|
|
392
|
+
localNetwork = null
|
|
393
|
+
networkReadiness.clear()
|
|
394
|
+
cancelReadinessTimeoutLocked()
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (manager != null) {
|
|
398
|
+
try {
|
|
399
|
+
manager.unregisterNetworkCallback(callback)
|
|
400
|
+
} catch (_: IllegalArgumentException) {
|
|
401
|
+
// Callback was not registered or has already been removed.
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
private fun rejectPendingConnect(code: String, message: String, cause: Throwable? = null) {
|
|
407
|
+
val promise: Promise?
|
|
408
|
+
synchronized(lock) {
|
|
409
|
+
promise = pendingConnectPromise
|
|
410
|
+
pendingConnectPromise = null
|
|
411
|
+
}
|
|
412
|
+
promise?.reject(code, message, cause)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
package com.glassly.bluetoothsdk
|
|
2
|
+
|
|
3
|
+
import java.util.UUID
|
|
4
|
+
import org.json.JSONObject
|
|
5
|
+
|
|
6
|
+
/** Observable state management with immediate event emission */
|
|
7
|
+
class ObservableStore {
|
|
8
|
+
companion object {
|
|
9
|
+
const val BLUETOOTH_CATEGORY = "bluetooth"
|
|
10
|
+
private const val LEGACY_CORE_CATEGORY = "core"
|
|
11
|
+
|
|
12
|
+
fun normalizeCategory(category: String): String =
|
|
13
|
+
if (category == LEGACY_CORE_CATEGORY) BLUETOOTH_CATEGORY else category
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private val values = mutableMapOf<String, Any>()
|
|
17
|
+
private val emitListeners = linkedMapOf<String, (String, Map<String, Any>) -> Unit>()
|
|
18
|
+
|
|
19
|
+
@Synchronized
|
|
20
|
+
fun configure(onEmit: (String, Map<String, Any>) -> Unit) {
|
|
21
|
+
emitListeners["default"] = onEmit
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@Synchronized
|
|
25
|
+
fun addListener(onEmit: (String, Map<String, Any>) -> Unit): String {
|
|
26
|
+
val id = UUID.randomUUID().toString()
|
|
27
|
+
emitListeners[id] = onEmit
|
|
28
|
+
return id
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Synchronized
|
|
32
|
+
fun removeListener(id: String) {
|
|
33
|
+
emitListeners.remove(id)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fun set(category: String, key: String, value: Any) {
|
|
37
|
+
val normalizedCategory: String
|
|
38
|
+
val listeners: List<(String, Map<String, Any>) -> Unit>
|
|
39
|
+
|
|
40
|
+
synchronized(this) {
|
|
41
|
+
normalizedCategory = normalizeCategory(category)
|
|
42
|
+
val fullKey = "$normalizedCategory.$key"
|
|
43
|
+
val oldValue = values[fullKey]
|
|
44
|
+
|
|
45
|
+
// Skip if unchanged
|
|
46
|
+
if (oldValue != null && toJson(oldValue) == toJson(value)) return
|
|
47
|
+
|
|
48
|
+
values[fullKey] = value
|
|
49
|
+
listeners = emitListeners.values.toList()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Emit immediately, outside the store lock so callbacks can safely re-enter the store.
|
|
53
|
+
listeners.forEach { it(normalizedCategory, mapOf(key to value)) }
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fun remove(category: String, key: String) {
|
|
57
|
+
val normalizedCategory: String
|
|
58
|
+
val updatedSnapshot: Map<String, Any>
|
|
59
|
+
val listeners: List<(String, Map<String, Any>) -> Unit>
|
|
60
|
+
|
|
61
|
+
synchronized(this) {
|
|
62
|
+
normalizedCategory = normalizeCategory(category)
|
|
63
|
+
val fullKey = "$normalizedCategory.$key"
|
|
64
|
+
if (!values.containsKey(fullKey)) return // already absent, no notification needed
|
|
65
|
+
values.remove(fullKey)
|
|
66
|
+
val prefix = "$normalizedCategory."
|
|
67
|
+
updatedSnapshot = values.filterKeys { it.startsWith(prefix) }
|
|
68
|
+
.mapKeys { it.key.removePrefix(prefix) }
|
|
69
|
+
listeners = emitListeners.values.toList()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Emit the full updated category snapshot so UI listeners can clear the removed key.
|
|
73
|
+
listeners.forEach { it(normalizedCategory, updatedSnapshot) }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@Synchronized
|
|
77
|
+
fun get(category: String, key: String): Any? = values["${normalizeCategory(category)}.$key"]
|
|
78
|
+
|
|
79
|
+
@Synchronized
|
|
80
|
+
fun getCategory(category: String): Map<String, Any> {
|
|
81
|
+
val prefix = "${normalizeCategory(category)}."
|
|
82
|
+
return values.filterKeys { it.startsWith(prefix) }.mapKeys { it.key.removePrefix(prefix) }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private fun toJson(value: Any): String {
|
|
86
|
+
return JSONObject(mapOf("v" to value)).toString()
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
package com.glassly.bluetoothsdk
|
|
2
|
+
|
|
3
|
+
import java.net.HttpURLConnection
|
|
4
|
+
import java.net.URI
|
|
5
|
+
import java.net.URL
|
|
6
|
+
import org.json.JSONArray
|
|
7
|
+
import org.json.JSONObject
|
|
8
|
+
|
|
9
|
+
internal object OtaManifestDefaults {
|
|
10
|
+
// ASG builds before 39 ignore ota_start.ota_version_url, so SDK checks must
|
|
11
|
+
// use the same legacy production manifest those glasses will install from.
|
|
12
|
+
const val LEGACY_PROD_OTA_VERSION_URL = "https://ota.glassly.ai/prod_live_version.json"
|
|
13
|
+
|
|
14
|
+
fun defaultOtaVersionUrl(): String {
|
|
15
|
+
val manifestUrl = GeneratedReleaseMetadata.OTA_MANIFEST_URL.trim()
|
|
16
|
+
if (manifestUrl.isBlank()) {
|
|
17
|
+
throw BluetoothSdkException(
|
|
18
|
+
"ota_manifest_unconfigured",
|
|
19
|
+
"This source-built Bluetooth SDK has no embedded OTA manifest. Configure one with the debug API.",
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
return manifestUrl
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
internal object OtaManifestChecker {
|
|
27
|
+
private const val ASG_CLIENT_PACKAGE = "com.mentra.asg_client"
|
|
28
|
+
|
|
29
|
+
fun normalizeHttpUrl(value: String): String {
|
|
30
|
+
val trimmed = value.trim()
|
|
31
|
+
if (trimmed.isEmpty()) {
|
|
32
|
+
throw BluetoothSdkException("invalid_ota_url", "OTA version URL must be a non-empty http(s) URL.")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
val uri =
|
|
36
|
+
try {
|
|
37
|
+
URI(trimmed)
|
|
38
|
+
} catch (error: Exception) {
|
|
39
|
+
throw BluetoothSdkException("invalid_ota_url", "OTA version URL must be a valid http(s) URL.", error)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
val scheme = uri.scheme?.lowercase()
|
|
43
|
+
if ((scheme != "http" && scheme != "https") || uri.host.isNullOrBlank()) {
|
|
44
|
+
throw BluetoothSdkException("invalid_ota_url", "OTA version URL must be an http(s) URL.")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return uri.toString()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fun fetch(otaVersionUrl: String): JSONObject {
|
|
51
|
+
val connection = URL(otaVersionUrl).openConnection() as HttpURLConnection
|
|
52
|
+
connection.connectTimeout = 15_000
|
|
53
|
+
connection.readTimeout = 15_000
|
|
54
|
+
connection.requestMethod = "GET"
|
|
55
|
+
return try {
|
|
56
|
+
val status = connection.responseCode
|
|
57
|
+
if (status !in 200..299) {
|
|
58
|
+
throw BluetoothSdkException("ota_manifest_request_failed", "OTA manifest request failed with HTTP $status for $otaVersionUrl.")
|
|
59
|
+
}
|
|
60
|
+
JSONObject(connection.inputStream.bufferedReader().use { it.readText() })
|
|
61
|
+
} finally {
|
|
62
|
+
connection.disconnect()
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
fun hasUpdate(
|
|
67
|
+
currentBuildNumber: String,
|
|
68
|
+
currentMtkVersion: String,
|
|
69
|
+
currentBesVersion: String,
|
|
70
|
+
manifest: JSONObject,
|
|
71
|
+
// Downgrade floor: a non-positive value (the default) disables downgrades entirely,
|
|
72
|
+
// matching ASG's fail-closed DowngradeGate and the engine. OEM SDK consumers that do
|
|
73
|
+
// not set a floor therefore get upgrade-only behavior and are never told a downgrade is
|
|
74
|
+
// available that the glasses would refuse.
|
|
75
|
+
downgradeFloorVersionCode: Long = 0L,
|
|
76
|
+
): Boolean =
|
|
77
|
+
hasApkUpdate(currentBuildNumber, manifest, downgradeFloorVersionCode) ||
|
|
78
|
+
hasMtkUpdate(manifest.optJSONArray("mtk_patches"), currentMtkVersion) ||
|
|
79
|
+
hasBesUpdate(manifest.optJSONObject("bes_firmware"), currentBesVersion)
|
|
80
|
+
|
|
81
|
+
fun hasMtkPatches(manifest: JSONObject): Boolean =
|
|
82
|
+
(manifest.optJSONArray("mtk_patches")?.length() ?: 0) > 0
|
|
83
|
+
|
|
84
|
+
fun hasBesFirmware(manifest: JSONObject): Boolean =
|
|
85
|
+
manifest.optJSONObject("bes_firmware") != null
|
|
86
|
+
|
|
87
|
+
private fun latestAppInfo(manifest: JSONObject): JSONObject {
|
|
88
|
+
val apps = manifest.optJSONObject("apps")
|
|
89
|
+
val app = apps?.optJSONObject(ASG_CLIENT_PACKAGE)
|
|
90
|
+
if (app != null && app.hasNumber("versionCode")) {
|
|
91
|
+
return app
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (manifest.hasNumber("versionCode")) {
|
|
95
|
+
return manifest
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
throw BluetoothSdkException("invalid_ota_manifest", "OTA manifest is missing ASG app versionCode.")
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private fun hasApkUpdate(
|
|
102
|
+
currentBuildNumber: String,
|
|
103
|
+
manifest: JSONObject,
|
|
104
|
+
downgradeFloorVersionCode: Long,
|
|
105
|
+
): Boolean {
|
|
106
|
+
val currentVersion =
|
|
107
|
+
currentBuildNumber.toLongOrNull()
|
|
108
|
+
?: throw BluetoothSdkException(
|
|
109
|
+
"invalid_glasses_version",
|
|
110
|
+
"Cannot check OTA update because glasses build number is invalid.",
|
|
111
|
+
)
|
|
112
|
+
// Only apps-shaped manifests are exact pins. A legacy top-level manifest (versionCode at
|
|
113
|
+
// the root, no apps entry) is NOT a pin and stays strictly upgrade-only, matching the
|
|
114
|
+
// engine TS checker — otherwise a build newer than such a manifest would report a false
|
|
115
|
+
// downgrade the JS path ignores.
|
|
116
|
+
val isExactPin = manifest.optJSONObject("apps")?.optJSONObject(ASG_CLIENT_PACKAGE)?.hasNumber("versionCode") == true
|
|
117
|
+
val serverVersion = latestAppInfo(manifest).requiredLong("versionCode")
|
|
118
|
+
if (!isExactPin) {
|
|
119
|
+
return serverVersion > currentVersion
|
|
120
|
+
}
|
|
121
|
+
// Exact pin: any mismatch is an update in either direction (downgrades take the
|
|
122
|
+
// uninstall-then-reinstall detour on the glasses). A non-positive pin is only legitimate
|
|
123
|
+
// in the frozen legacy rescue manifests that pre-39 glasses are checked against; for
|
|
124
|
+
// modern glasses it means the manifest cannot verify anything, and that must surface as an
|
|
125
|
+
// error — never as "no update".
|
|
126
|
+
if (serverVersion <= 0) {
|
|
127
|
+
if (currentVersion >= 39) {
|
|
128
|
+
throw BluetoothSdkException(
|
|
129
|
+
"invalid_ota_manifest",
|
|
130
|
+
"OTA manifest ASG pin is missing or zero; cannot verify update state.",
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
if (serverVersion > currentVersion) {
|
|
136
|
+
return true
|
|
137
|
+
}
|
|
138
|
+
// Exact match: already on the pin, no update.
|
|
139
|
+
if (serverVersion == currentVersion) {
|
|
140
|
+
return false
|
|
141
|
+
}
|
|
142
|
+
// Downgrade: only actionable at/above the enabled floor; a non-positive floor disables
|
|
143
|
+
// downgrades (fail closed, matching ASG and the engine).
|
|
144
|
+
return downgradeFloorVersionCode > 0 && serverVersion >= downgradeFloorVersionCode
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private fun hasMtkUpdate(patches: JSONArray?, currentVersion: String): Boolean {
|
|
148
|
+
if (patches == null || patches.length() == 0) return false
|
|
149
|
+
if (currentVersion.isBlank()) return false
|
|
150
|
+
|
|
151
|
+
for (index in 0 until patches.length()) {
|
|
152
|
+
val patch = patches.optJSONObject(index) ?: continue
|
|
153
|
+
val startFirmware = patch.optString("start_firmware", "")
|
|
154
|
+
if (startFirmware == currentVersion) return true
|
|
155
|
+
val serverDate = if (startFirmware.contains("_")) startFirmware.substringAfterLast("_") else startFirmware
|
|
156
|
+
if (serverDate == currentVersion) return true
|
|
157
|
+
}
|
|
158
|
+
return false
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private fun hasBesUpdate(besFirmware: JSONObject?, currentVersion: String): Boolean {
|
|
162
|
+
if (besFirmware == null) return false
|
|
163
|
+
val serverVersion = besFirmware.optString("version", "")
|
|
164
|
+
if (serverVersion.isBlank()) {
|
|
165
|
+
throw BluetoothSdkException("invalid_ota_manifest", "OTA manifest bes_firmware.version is missing.")
|
|
166
|
+
}
|
|
167
|
+
if (currentVersion.isBlank()) return true
|
|
168
|
+
return compareVersions(serverVersion, currentVersion) > 0
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private fun compareVersions(version1: String, version2: String): Int {
|
|
172
|
+
if (version1.contains(".") && version2.contains(".")) {
|
|
173
|
+
val parts1 = version1.split(".")
|
|
174
|
+
val parts2 = version2.split(".")
|
|
175
|
+
val maxLength = maxOf(parts1.size, parts2.size)
|
|
176
|
+
for (index in 0 until maxLength) {
|
|
177
|
+
val value1 = parts1.getOrNull(index)?.toIntOrNull() ?: 0
|
|
178
|
+
val value2 = parts2.getOrNull(index)?.toIntOrNull() ?: 0
|
|
179
|
+
if (value1 != value2) return value1 - value2
|
|
180
|
+
}
|
|
181
|
+
return 0
|
|
182
|
+
}
|
|
183
|
+
return version1.compareTo(version2)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
private fun JSONObject.hasNumber(key: String): Boolean = !isNull(key) && opt(key) is Number
|
|
187
|
+
|
|
188
|
+
private fun JSONObject.requiredLong(key: String): Long =
|
|
189
|
+
(opt(key) as? Number)?.toLong()
|
|
190
|
+
?: throw BluetoothSdkException("invalid_ota_manifest", "OTA manifest is missing ASG app versionCode.")
|
|
191
|
+
}
|