@mentra/engine 0.1.0-dev.0 → 0.1.0-dev.1
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 +42 -1
- package/build/engine.d.ts +11 -3
- package/build/engine.d.ts.map +1 -1
- package/build/engine.js +7 -2
- package/build/engine.js.map +1 -1
- package/build/facades/dev.d.ts +3 -2
- package/build/facades/dev.d.ts.map +1 -1
- package/build/facades/dev.js +18 -12
- package/build/facades/dev.js.map +1 -1
- package/build/facades/gallery.js +1 -1
- package/build/facades/gallery.js.map +1 -1
- package/build/facades/glasses.d.ts +1 -0
- package/build/facades/glasses.d.ts.map +1 -1
- package/build/facades/glasses.js +19 -4
- package/build/facades/glasses.js.map +1 -1
- package/build/facades/glassesSettings.d.ts.map +1 -1
- package/build/facades/glassesSettings.js +1 -0
- package/build/facades/glassesSettings.js.map +1 -1
- package/build/facades/miniapps.d.ts +4 -0
- package/build/facades/miniapps.d.ts.map +1 -1
- package/build/facades/miniapps.js +5 -0
- package/build/facades/miniapps.js.map +1 -1
- package/build/facades/ota.d.ts +11 -2
- package/build/facades/ota.d.ts.map +1 -1
- package/build/facades/ota.js +19 -4
- package/build/facades/ota.js.map +1 -1
- package/build/facades/pairing.d.ts +4 -0
- package/build/facades/pairing.d.ts.map +1 -1
- package/build/facades/pairing.js +10 -8
- package/build/facades/pairing.js.map +1 -1
- package/build/facades/permissions.d.ts +10 -0
- package/build/facades/permissions.d.ts.map +1 -1
- package/build/facades/permissions.js +10 -0
- package/build/facades/permissions.js.map +1 -1
- package/build/facades/phoneNotifications.d.ts +0 -4
- package/build/facades/phoneNotifications.d.ts.map +1 -1
- package/build/facades/phoneNotifications.js +4 -14
- package/build/facades/phoneNotifications.js.map +1 -1
- package/build/index.d.ts +6 -3
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -2
- package/build/index.js.map +1 -1
- package/build/internal.d.ts +3 -2
- package/build/internal.d.ts.map +1 -1
- package/build/internal.js +2 -1
- package/build/internal.js.map +1 -1
- package/build/react/MentraLiveOtaFlow.d.ts +38 -0
- package/build/react/MentraLiveOtaFlow.d.ts.map +1 -0
- package/build/react/MentraLiveOtaFlow.js +528 -0
- package/build/react/MentraLiveOtaFlow.js.map +1 -0
- package/build/react/index.d.ts +2 -0
- package/build/react/index.d.ts.map +1 -0
- package/build/react/index.js +2 -0
- package/build/react/index.js.map +1 -0
- package/build/react/useEngineSnapshot.d.ts +2 -0
- package/build/react/useEngineSnapshot.d.ts.map +1 -0
- package/build/react/useEngineSnapshot.js +14 -0
- package/build/react/useEngineSnapshot.js.map +1 -0
- package/build/runtime/bootstrap.d.ts +24 -1
- package/build/runtime/bootstrap.d.ts.map +1 -1
- package/build/runtime/bootstrap.js +17 -0
- package/build/runtime/bootstrap.js.map +1 -1
- package/build/runtime/scanQrSeam.d.ts +7 -0
- package/build/runtime/scanQrSeam.d.ts.map +1 -0
- package/build/runtime/scanQrSeam.js +16 -0
- package/build/runtime/scanQrSeam.js.map +1 -0
- package/build/services/AppRegistry.d.ts +33 -57
- package/build/services/AppRegistry.d.ts.map +1 -1
- package/build/services/AppRegistry.js +193 -144
- package/build/services/AppRegistry.js.map +1 -1
- package/build/services/AudioCloudUplink.d.ts +12 -0
- package/build/services/AudioCloudUplink.d.ts.map +1 -1
- package/build/services/AudioCloudUplink.js +22 -1
- package/build/services/AudioCloudUplink.js.map +1 -1
- package/build/services/AudioPlaybackService.d.ts +41 -2
- package/build/services/AudioPlaybackService.d.ts.map +1 -1
- package/build/services/AudioPlaybackService.js +250 -33
- package/build/services/AudioPlaybackService.js.map +1 -1
- package/build/services/BlobStore.d.ts +2 -1
- package/build/services/BlobStore.d.ts.map +1 -1
- package/build/services/BlobStore.js +17 -9
- package/build/services/BlobStore.js.map +1 -1
- package/build/services/CloudAudioSubscriptionSync.d.ts.map +1 -1
- package/build/services/CloudAudioSubscriptionSync.js +13 -1
- package/build/services/CloudAudioSubscriptionSync.js.map +1 -1
- package/build/services/CloudClientService.d.ts +15 -0
- package/build/services/CloudClientService.d.ts.map +1 -1
- package/build/services/CloudClientService.js +93 -1
- package/build/services/CloudClientService.js.map +1 -1
- package/build/services/DeviceEventRouter.js +5 -5
- package/build/services/DeviceEventRouter.js.map +1 -1
- package/build/services/ForegroundLocationPermission.d.ts +13 -0
- package/build/services/ForegroundLocationPermission.d.ts.map +1 -0
- package/build/services/ForegroundLocationPermission.js +12 -0
- package/build/services/ForegroundLocationPermission.js.map +1 -0
- package/build/services/GlassesSettingsSync.d.ts.map +1 -1
- package/build/services/GlassesSettingsSync.js +9 -3
- package/build/services/GlassesSettingsSync.js.map +1 -1
- package/build/services/GlassesStatusProjection.d.ts +2 -1
- package/build/services/GlassesStatusProjection.d.ts.map +1 -1
- package/build/services/GlassesStatusProjection.js +15 -8
- package/build/services/GlassesStatusProjection.js.map +1 -1
- package/build/services/HotspotOtaTransport.d.ts +29 -0
- package/build/services/HotspotOtaTransport.d.ts.map +1 -0
- package/build/services/HotspotOtaTransport.js +146 -0
- package/build/services/HotspotOtaTransport.js.map +1 -0
- package/build/services/HotspotShutdown.d.ts +17 -0
- package/build/services/HotspotShutdown.d.ts.map +1 -0
- package/build/services/HotspotShutdown.js +29 -0
- package/build/services/HotspotShutdown.js.map +1 -0
- package/build/services/LocalDisplayManager.d.ts +15 -0
- package/build/services/LocalDisplayManager.d.ts.map +1 -1
- package/build/services/LocalDisplayManager.js +106 -20
- package/build/services/LocalDisplayManager.js.map +1 -1
- package/build/services/LocalMiniappRuntime.d.ts +74 -2
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +785 -291
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/LocalMiniappStorage.d.ts +32 -0
- package/build/services/LocalMiniappStorage.d.ts.map +1 -0
- package/build/services/LocalMiniappStorage.js +66 -0
- package/build/services/LocalMiniappStorage.js.map +1 -0
- package/build/services/LocalMiniappUserIdentity.d.ts +20 -0
- package/build/services/LocalMiniappUserIdentity.d.ts.map +1 -0
- package/build/services/LocalMiniappUserIdentity.js +43 -0
- package/build/services/LocalMiniappUserIdentity.js.map +1 -0
- package/build/services/LocalSttFallbackCoordinator.d.ts +9 -6
- package/build/services/LocalSttFallbackCoordinator.d.ts.map +1 -1
- package/build/services/LocalSttFallbackCoordinator.js +18 -12
- package/build/services/LocalSttFallbackCoordinator.js.map +1 -1
- package/build/services/MicStateCoordinator.d.ts +42 -1
- package/build/services/MicStateCoordinator.d.ts.map +1 -1
- package/build/services/MicStateCoordinator.js +141 -3
- package/build/services/MicStateCoordinator.js.map +1 -1
- package/build/services/MiniappLauncher.d.ts.map +1 -1
- package/build/services/MiniappLauncher.js +23 -3
- package/build/services/MiniappLauncher.js.map +1 -1
- package/build/services/MiniappLiveness.d.ts +12 -0
- package/build/services/MiniappLiveness.d.ts.map +1 -0
- package/build/services/MiniappLiveness.js +16 -0
- package/build/services/MiniappLiveness.js.map +1 -0
- package/build/services/NativeHttpResponse.d.ts +3 -0
- package/build/services/NativeHttpResponse.d.ts.map +1 -0
- package/build/services/NativeHttpResponse.js +6 -0
- package/build/services/NativeHttpResponse.js.map +1 -0
- package/build/services/OtaArtifactDownloader.d.ts +65 -0
- package/build/services/OtaArtifactDownloader.d.ts.map +1 -0
- package/build/services/OtaArtifactDownloader.js +226 -0
- package/build/services/OtaArtifactDownloader.js.map +1 -0
- package/build/services/OtaAutoChain.d.ts +34 -0
- package/build/services/OtaAutoChain.d.ts.map +1 -0
- package/build/services/OtaAutoChain.js +83 -0
- package/build/services/OtaAutoChain.js.map +1 -0
- package/build/services/OtaErrorMapping.d.ts +18 -0
- package/build/services/OtaErrorMapping.d.ts.map +1 -0
- package/build/services/OtaErrorMapping.js +63 -0
- package/build/services/OtaErrorMapping.js.map +1 -0
- package/build/services/OtaInstallCoordinator.d.ts +94 -14
- package/build/services/OtaInstallCoordinator.d.ts.map +1 -1
- package/build/services/OtaInstallCoordinator.js +574 -130
- package/build/services/OtaInstallCoordinator.js.map +1 -1
- package/build/services/OtaService.d.ts.map +1 -1
- package/build/services/OtaService.js +8 -1
- package/build/services/OtaService.js.map +1 -1
- package/build/services/OtaUpdateCheckService.d.ts +38 -3
- package/build/services/OtaUpdateCheckService.d.ts.map +1 -1
- package/build/services/OtaUpdateCheckService.js +142 -22
- package/build/services/OtaUpdateCheckService.js.map +1 -1
- package/build/services/PhoneCalendarHelpers.d.ts +32 -0
- package/build/services/PhoneCalendarHelpers.d.ts.map +1 -0
- package/build/services/PhoneCalendarHelpers.js +157 -0
- package/build/services/PhoneCalendarHelpers.js.map +1 -0
- package/build/services/PhoneCalendarService.d.ts +5 -0
- package/build/services/PhoneCalendarService.d.ts.map +1 -0
- package/build/services/PhoneCalendarService.js +45 -0
- package/build/services/PhoneCalendarService.js.map +1 -0
- package/build/services/PhoneCameraFovCoordinator.d.ts +30 -0
- package/build/services/PhoneCameraFovCoordinator.d.ts.map +1 -0
- package/build/services/PhoneCameraFovCoordinator.js +194 -0
- package/build/services/PhoneCameraFovCoordinator.js.map +1 -0
- package/build/services/PhoneNotificationsSync.d.ts.map +1 -1
- package/build/services/PhoneNotificationsSync.js +12 -10
- package/build/services/PhoneNotificationsSync.js.map +1 -1
- package/build/services/PhonePhotoCoordinator.d.ts +26 -2
- package/build/services/PhonePhotoCoordinator.d.ts.map +1 -1
- package/build/services/PhonePhotoCoordinator.js +79 -9
- package/build/services/PhonePhotoCoordinator.js.map +1 -1
- package/build/services/PhoneStreamCoordinator.d.ts +8 -1
- package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
- package/build/services/PhoneStreamCoordinator.js +125 -13
- package/build/services/PhoneStreamCoordinator.js.map +1 -1
- package/build/services/PhoneVideoCoordinator.d.ts +2 -0
- package/build/services/PhoneVideoCoordinator.d.ts.map +1 -1
- package/build/services/PhoneVideoCoordinator.js +10 -0
- package/build/services/PhoneVideoCoordinator.js.map +1 -1
- package/build/services/SceneRenderer.d.ts.map +1 -1
- package/build/services/SceneRenderer.js +15 -0
- package/build/services/SceneRenderer.js.map +1 -1
- package/build/services/SentenceTtsPipeline.d.ts +31 -0
- package/build/services/SentenceTtsPipeline.d.ts.map +1 -0
- package/build/services/SentenceTtsPipeline.js +67 -0
- package/build/services/SentenceTtsPipeline.js.map +1 -0
- package/build/services/SupportProfileSync.d.ts +22 -0
- package/build/services/SupportProfileSync.d.ts.map +1 -0
- package/build/services/SupportProfileSync.js +213 -0
- package/build/services/SupportProfileSync.js.map +1 -0
- package/build/services/TranscriptionRouting.d.ts +12 -0
- package/build/services/TranscriptionRouting.d.ts.map +1 -0
- package/build/services/TranscriptionRouting.js +24 -0
- package/build/services/TranscriptionRouting.js.map +1 -0
- package/build/services/TtsTextSanitizer.d.ts +5 -0
- package/build/services/TtsTextSanitizer.d.ts.map +1 -0
- package/build/services/TtsTextSanitizer.js +61 -0
- package/build/services/TtsTextSanitizer.js.map +1 -0
- package/build/services/WifiAdbRequest.d.ts +17 -0
- package/build/services/WifiAdbRequest.d.ts.map +1 -0
- package/build/services/WifiAdbRequest.js +35 -0
- package/build/services/WifiAdbRequest.js.map +1 -0
- package/build/services/asg/asgCameraApi.d.ts +43 -0
- package/build/services/asg/asgCameraApi.d.ts.map +1 -1
- package/build/services/asg/asgCameraApi.js +509 -26
- package/build/services/asg/asgCameraApi.js.map +1 -1
- package/build/services/asg/cameraRollExportCoordinator.d.ts +60 -0
- package/build/services/asg/cameraRollExportCoordinator.d.ts.map +1 -0
- package/build/services/asg/cameraRollExportCoordinator.js +618 -0
- package/build/services/asg/cameraRollExportCoordinator.js.map +1 -0
- package/build/services/asg/cameraRollExportLedger.d.ts +44 -0
- package/build/services/asg/cameraRollExportLedger.d.ts.map +1 -0
- package/build/services/asg/cameraRollExportLedger.js +78 -0
- package/build/services/asg/cameraRollExportLedger.js.map +1 -0
- package/build/services/asg/galleryNotices.d.ts +1 -1
- package/build/services/asg/galleryNotices.d.ts.map +1 -1
- package/build/services/asg/galleryNotices.js.map +1 -1
- package/build/services/asg/galleryPathMigration.d.ts +3 -0
- package/build/services/asg/galleryPathMigration.d.ts.map +1 -0
- package/build/services/asg/galleryPathMigration.js +26 -0
- package/build/services/asg/galleryPathMigration.js.map +1 -0
- package/build/services/asg/gallerySettingsService.d.ts +1 -0
- package/build/services/asg/gallerySettingsService.d.ts.map +1 -1
- package/build/services/asg/gallerySettingsService.js +2 -0
- package/build/services/asg/gallerySettingsService.js.map +1 -1
- package/build/services/asg/gallerySyncService.d.ts.map +1 -1
- package/build/services/asg/gallerySyncService.js +162 -56
- package/build/services/asg/gallerySyncService.js.map +1 -1
- package/build/services/asg/galleryTransferLedger.d.ts +58 -0
- package/build/services/asg/galleryTransferLedger.d.ts.map +1 -0
- package/build/services/asg/galleryTransferLedger.js +225 -0
- package/build/services/asg/galleryTransferLedger.js.map +1 -0
- package/build/services/asg/localNetworkTransport.d.ts +15 -0
- package/build/services/asg/localNetworkTransport.d.ts.map +1 -0
- package/build/services/asg/localNetworkTransport.js +144 -0
- package/build/services/asg/localNetworkTransport.js.map +1 -0
- package/build/services/asg/localStorageService.d.ts +18 -0
- package/build/services/asg/localStorageService.d.ts.map +1 -1
- package/build/services/asg/localStorageService.js +168 -19
- package/build/services/asg/localStorageService.js.map +1 -1
- package/build/services/asg/mediaProcessingQueue.d.ts +10 -0
- package/build/services/asg/mediaProcessingQueue.d.ts.map +1 -1
- package/build/services/asg/mediaProcessingQueue.js +193 -62
- package/build/services/asg/mediaProcessingQueue.js.map +1 -1
- package/build/services/audioPlaybackAssets.d.ts +8 -0
- package/build/services/audioPlaybackAssets.d.ts.map +1 -0
- package/build/services/audioPlaybackAssets.js +7 -0
- package/build/services/audioPlaybackAssets.js.map +1 -0
- package/build/services/gallerySyncClock.js +1 -1
- package/build/services/gallerySyncClock.js.map +1 -1
- package/build/services/glassesClockSync.js +2 -2
- package/build/services/glassesClockSync.js.map +1 -1
- package/build/services/manifestActions.d.ts +7 -0
- package/build/services/manifestActions.d.ts.map +1 -0
- package/build/services/manifestActions.js +25 -0
- package/build/services/manifestActions.js.map +1 -0
- package/build/services/otaDisplayState.d.ts +22 -11
- package/build/services/otaDisplayState.d.ts.map +1 -1
- package/build/services/otaDisplayState.js +36 -14
- package/build/services/otaDisplayState.js.map +1 -1
- package/build/services/otaInstallPolicy.d.ts +15 -3
- package/build/services/otaInstallPolicy.d.ts.map +1 -1
- package/build/services/otaInstallPolicy.js +24 -3
- package/build/services/otaInstallPolicy.js.map +1 -1
- package/build/services/otaManifestUrl.d.ts +2 -0
- package/build/services/otaManifestUrl.d.ts.map +1 -0
- package/build/services/{asgOtaVersionUrl.js → otaManifestUrl.js} +15 -5
- package/build/services/otaManifestUrl.js.map +1 -0
- package/build/services/slimStreamStatus.d.ts +11 -0
- package/build/services/slimStreamStatus.d.ts.map +1 -1
- package/build/services/slimStreamStatus.js +14 -4
- package/build/services/slimStreamStatus.js.map +1 -1
- package/build/stores/apps.d.ts.map +1 -1
- package/build/stores/apps.js +11 -0
- package/build/stores/apps.js.map +1 -1
- package/build/stores/bluetoothSettingKeys.d.ts.map +1 -1
- package/build/stores/bluetoothSettingKeys.js +2 -0
- package/build/stores/bluetoothSettingKeys.js.map +1 -1
- package/build/stores/gallerySync.d.ts +2 -0
- package/build/stores/gallerySync.d.ts.map +1 -1
- package/build/stores/gallerySync.js +2 -0
- package/build/stores/gallerySync.js.map +1 -1
- package/build/stores/glasses.d.ts.map +1 -1
- package/build/stores/glasses.js +3 -0
- package/build/stores/glasses.js.map +1 -1
- package/build/stores/settings.d.ts +17 -0
- package/build/stores/settings.d.ts.map +1 -1
- package/build/stores/settings.js +157 -17
- package/build/stores/settings.js.map +1 -1
- package/build/types/applet.d.ts +2 -0
- package/build/types/applet.d.ts.map +1 -1
- package/build/types/applet.js.map +1 -1
- package/build/types/asg.d.ts +7 -0
- package/build/types/asg.d.ts.map +1 -1
- package/build/types/asg.js.map +1 -1
- package/build/types/enums.d.ts +1 -0
- package/build/types/enums.d.ts.map +1 -1
- package/build/types/enums.js +1 -0
- package/build/types/enums.js.map +1 -1
- package/build/types/hardware.d.ts +1 -0
- package/build/types/hardware.d.ts.map +1 -1
- package/build/types/hardware.js +14 -0
- package/build/types/hardware.js.map +1 -1
- package/build/utils/configuredDevHost.d.ts +6 -0
- package/build/utils/configuredDevHost.d.ts.map +1 -0
- package/build/utils/configuredDevHost.js +19 -0
- package/build/utils/configuredDevHost.js.map +1 -0
- package/build/utils/devMiniappLaunch.d.ts +19 -8
- package/build/utils/devMiniappLaunch.d.ts.map +1 -1
- package/build/utils/devMiniappLaunch.js +144 -21
- package/build/utils/devMiniappLaunch.js.map +1 -1
- package/build/utils/diagnosticContext.d.ts.map +1 -1
- package/build/utils/diagnosticContext.js +15 -17
- package/build/utils/diagnosticContext.js.map +1 -1
- package/build/utils/hardware/hardware.d.ts.map +1 -1
- package/build/utils/hardware/hardware.js +7 -3
- package/build/utils/hardware/hardware.js.map +1 -1
- package/build/utils/miniappDiagnostics.d.ts +64 -0
- package/build/utils/miniappDiagnostics.d.ts.map +1 -0
- package/build/utils/miniappDiagnostics.js +173 -0
- package/build/utils/miniappDiagnostics.js.map +1 -0
- package/build/utils/permissions/MediaLibraryPermissions.d.ts +11 -0
- package/build/utils/permissions/MediaLibraryPermissions.d.ts.map +1 -1
- package/build/utils/permissions/MediaLibraryPermissions.js +27 -4
- package/build/utils/permissions/MediaLibraryPermissions.js.map +1 -1
- package/build/utils/timers/timers.d.ts.map +1 -1
- package/build/utils/timers/timers.js +55 -18
- package/build/utils/timers/timers.js.map +1 -1
- package/package.json +25 -15
- package/src/assets/silence.wav +0 -0
- package/src/engine.ts +7 -2
- package/src/facades/dev.ts +17 -12
- package/src/facades/gallery.ts +1 -1
- package/src/facades/glasses.ts +17 -4
- package/src/facades/glassesSettings.ts +5 -0
- package/src/facades/miniapps.ts +7 -0
- package/src/facades/ota.ts +19 -4
- package/src/facades/pairing.ts +11 -7
- package/src/facades/permissions.ts +14 -0
- package/src/facades/phoneNotifications.ts +4 -13
- package/src/index.ts +26 -2
- package/src/internal.ts +3 -1
- package/src/react/MentraLiveOtaFlow.tsx +856 -0
- package/src/react/index.ts +7 -0
- package/src/react/useEngineSnapshot.ts +16 -0
- package/src/runtime/bootstrap.ts +33 -1
- package/src/runtime/scanQrSeam.ts +20 -0
- package/src/services/AppRegistry.ts +224 -149
- package/src/services/AudioCloudUplink.ts +23 -1
- package/src/services/AudioPlaybackService.ts +287 -38
- package/src/services/BlobStore.ts +22 -9
- package/src/services/CloudAudioSubscriptionSync.ts +13 -1
- package/src/services/CloudClientService.ts +96 -1
- package/src/services/DeviceEventRouter.ts +5 -5
- package/src/services/ForegroundLocationPermission.ts +24 -0
- package/src/services/GlassesSettingsSync.ts +9 -3
- package/src/services/GlassesStatusProjection.ts +16 -8
- package/src/services/HotspotOtaTransport.ts +184 -0
- package/src/services/HotspotShutdown.ts +41 -0
- package/src/services/LocalDisplayManager.ts +112 -21
- package/src/services/LocalMiniappRuntime.ts +967 -346
- package/src/services/LocalMiniappStorage.ts +82 -0
- package/src/services/LocalMiniappUserIdentity.ts +46 -0
- package/src/services/LocalSttFallbackCoordinator.ts +19 -12
- package/src/services/MicStateCoordinator.ts +169 -4
- package/src/services/MiniappLauncher.ts +30 -3
- package/src/services/MiniappLiveness.ts +24 -0
- package/src/services/NativeHttpResponse.ts +6 -0
- package/src/services/OtaArtifactDownloader.ts +312 -0
- package/src/services/OtaAutoChain.ts +106 -0
- package/src/services/OtaErrorMapping.ts +80 -0
- package/src/services/OtaInstallCoordinator.ts +645 -143
- package/src/services/OtaService.ts +11 -5
- package/src/services/OtaUpdateCheckService.ts +180 -20
- package/src/services/PhoneCalendarHelpers.ts +212 -0
- package/src/services/PhoneCalendarService.ts +65 -0
- package/src/services/PhoneCameraFovCoordinator.ts +210 -0
- package/src/services/PhoneNotificationsSync.ts +12 -10
- package/src/services/PhonePhotoCoordinator.ts +116 -18
- package/src/services/PhoneStreamCoordinator.ts +139 -25
- package/src/services/PhoneVideoCoordinator.ts +11 -0
- package/src/services/SceneRenderer.ts +16 -0
- package/src/services/SentenceTtsPipeline.ts +106 -0
- package/src/services/SupportProfileSync.ts +218 -0
- package/src/services/TranscriptionRouting.ts +37 -0
- package/src/services/TtsTextSanitizer.ts +66 -0
- package/src/services/WifiAdbRequest.ts +53 -0
- package/src/services/asg/asgCameraApi.ts +615 -33
- package/src/services/asg/cameraRollExportCoordinator.ts +627 -0
- package/src/services/asg/cameraRollExportLedger.ts +122 -0
- package/src/services/asg/galleryNotices.ts +1 -0
- package/src/services/asg/galleryPathMigration.ts +22 -0
- package/src/services/asg/gallerySettingsService.ts +3 -0
- package/src/services/asg/gallerySyncService.ts +184 -73
- package/src/services/asg/galleryTransferLedger.ts +312 -0
- package/src/services/asg/localNetworkTransport.ts +169 -0
- package/src/services/asg/localStorageService.ts +179 -24
- package/src/services/asg/mediaProcessingQueue.ts +209 -62
- package/src/services/audioPlaybackAssets.ts +8 -0
- package/src/services/gallerySyncClock.ts +1 -1
- package/src/services/glassesClockSync.ts +2 -2
- package/src/services/manifestActions.ts +30 -0
- package/src/services/otaDisplayState.ts +57 -16
- package/src/services/otaInstallPolicy.ts +29 -3
- package/src/services/{asgOtaVersionUrl.ts → otaManifestUrl.ts} +16 -4
- package/src/services/slimStreamStatus.ts +16 -5
- package/src/stores/apps.ts +12 -0
- package/src/stores/bluetoothSettingKeys.ts +2 -0
- package/src/stores/gallerySync.ts +4 -0
- package/src/stores/glasses.ts +3 -0
- package/src/stores/settings.ts +179 -18
- package/src/types/applet.ts +2 -0
- package/src/types/asg.ts +7 -0
- package/src/types/enums.ts +2 -1
- package/src/types/hardware.ts +17 -0
- package/src/utils/configuredDevHost.ts +17 -0
- package/src/utils/devMiniappLaunch.ts +151 -23
- package/src/utils/diagnosticContext.ts +17 -17
- package/src/utils/hardware/hardware.ts +7 -9
- package/src/utils/miniappDiagnostics.ts +226 -0
- package/src/utils/permissions/MediaLibraryPermissions.ts +35 -4
- package/src/utils/timers/timers.ts +57 -21
- package/build/services/asgOtaVersionUrl.d.ts +0 -2
- package/build/services/asgOtaVersionUrl.d.ts.map +0 -1
- package/build/services/asgOtaVersionUrl.js.map +0 -1
|
@@ -13,21 +13,26 @@
|
|
|
13
13
|
* The local-miniapp path drives the cloud's transcription/translation through
|
|
14
14
|
* this service.
|
|
15
15
|
*/
|
|
16
|
-
import {CloudClient, setNativeUdp, setSecureStorage} from "@mentra/cloud-client/react-native"
|
|
16
|
+
import {CloudClient, setNativeHttp, setNativeUdp, setSecureStorage} from "@mentra/cloud-client/react-native"
|
|
17
17
|
import type {PreinstalledMiniappRegistry, RuntimeSnapshot} from "@mentra/cloud-client/react-native"
|
|
18
18
|
import type {SubjectTokenType} from "@mentra/cloud-client"
|
|
19
|
+
import {Platform} from "react-native"
|
|
19
20
|
import type {AudioSubscription, TranscriptionData, TranslationData} from "@mentra/cloud-protocol"
|
|
20
21
|
|
|
21
22
|
import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
|
|
23
|
+
import CrustModule from "@mentra/crust"
|
|
22
24
|
import {getAuth, getConfigValues} from "../runtime/bootstrap"
|
|
23
25
|
import {useSettingsStore, SETTINGS} from "../stores/settings"
|
|
24
26
|
import {type CloudClientStatusSnapshot, type MiniappAuthToken} from "../runtime/config"
|
|
25
27
|
import {createCloudUdpSocket} from "../utils/cloudClient/RnUdpAdapter"
|
|
26
28
|
import {cloudSecureStore} from "../utils/cloudClient/cloudSecureStore"
|
|
29
|
+
import {storage as mmkvStorage} from "../utils/storage"
|
|
27
30
|
import {useCloudClientStatusStore} from "../stores/cloudClientStatus"
|
|
28
31
|
import {islandNotifications} from "./NotificationsEmitter"
|
|
29
32
|
import {BgTimer} from "../utils/timers"
|
|
30
33
|
import {logCloudV2TranscriptMetric} from "./CloudTranscriptE2EMetrics"
|
|
34
|
+
import {LocalMiniappUserIdentity} from "./LocalMiniappUserIdentity"
|
|
35
|
+
import {nativeHttpResponseBody} from "./NativeHttpResponse"
|
|
31
36
|
|
|
32
37
|
const LOG_TAG = "cloudClient"
|
|
33
38
|
type CloudCore = NonNullable<CloudClient["core"]>
|
|
@@ -37,6 +42,7 @@ type CloudCore = NonNullable<CloudClient["core"]>
|
|
|
37
42
|
// been continuously NOT connected for this long. A quick reconnect cancels it; brief
|
|
38
43
|
// flaps that recover don't fire. Raised as engine.notifications(connection_failed_persistent).
|
|
39
44
|
const CLOUD_PERSISTENT_FAILURE_MS = 60_000
|
|
45
|
+
const LOCAL_MINIAPP_USER_ID_KEY = "mentra.localMiniapp.userId"
|
|
40
46
|
|
|
41
47
|
/** Cancel the pending persistent-failure alarm + re-arm for the next outage. */
|
|
42
48
|
function clearPersistentFailureAlarm(): void {
|
|
@@ -72,6 +78,21 @@ let localDevRuntimeToken: {runtimeUrl: string; token: string; expiresAtMs: numbe
|
|
|
72
78
|
let lc3FrameSizeUnsubscribe: (() => void) | null = null
|
|
73
79
|
let coreTokenSyncPromise: Promise<string> | null = null
|
|
74
80
|
|
|
81
|
+
const localMiniappUserIdentity = new LocalMiniappUserIdentity({
|
|
82
|
+
get(): string | null {
|
|
83
|
+
const result = mmkvStorage.load<string>(LOCAL_MINIAPP_USER_ID_KEY)
|
|
84
|
+
return result.is_ok() ? result.value : null
|
|
85
|
+
},
|
|
86
|
+
set(userId: string): void {
|
|
87
|
+
const result = mmkvStorage.save(LOCAL_MINIAPP_USER_ID_KEY, userId)
|
|
88
|
+
if (result.is_error()) throw result.error
|
|
89
|
+
},
|
|
90
|
+
remove(): void {
|
|
91
|
+
const result = mmkvStorage.remove(LOCAL_MINIAPP_USER_ID_KEY)
|
|
92
|
+
if (result.is_error()) throw result.error
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
|
|
75
96
|
const transcriptListeners = new Set<(d: TranscriptionData) => void>()
|
|
76
97
|
const translationListeners = new Set<(d: TranslationData) => void>()
|
|
77
98
|
const statusListeners = new Set<(snapshot: CloudClientStatusSnapshot) => void>()
|
|
@@ -111,6 +132,7 @@ async function syncCoreAccessTokenToBluetoothInternal(): Promise<string> {
|
|
|
111
132
|
if (c !== client) {
|
|
112
133
|
throw new Error("cloud client changed while syncing core token")
|
|
113
134
|
}
|
|
135
|
+
localMiniappUserIdentity.remember(c.auth.identity.mentraUserId)
|
|
114
136
|
const result = await useSettingsStore.getState().setSetting(SETTINGS.core_token.key, token, false)
|
|
115
137
|
if (result.is_error()) {
|
|
116
138
|
throw result.error
|
|
@@ -261,6 +283,13 @@ function ensureAuthWatch(): void {
|
|
|
261
283
|
let reconnectPending = false
|
|
262
284
|
try {
|
|
263
285
|
const sub = auth.onStateChange((event, session) => {
|
|
286
|
+
if (event === "SIGNED_OUT") {
|
|
287
|
+
localMiniappUserIdentity.forget()
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
// A new sign-in may belong to a different account. Force the next
|
|
291
|
+
// storage request to resolve and persist that account's Core identity.
|
|
292
|
+
if (event === "SIGNED_IN") localMiniappUserIdentity.forget()
|
|
264
293
|
if (!session?.token) return
|
|
265
294
|
if (connected || reconnectPending) return
|
|
266
295
|
|
|
@@ -322,6 +351,24 @@ function ensureTransports(): void {
|
|
|
322
351
|
transportsReady = true
|
|
323
352
|
setNativeUdp(() => createCloudUdpSocket())
|
|
324
353
|
setSecureStorage(cloudSecureStore)
|
|
354
|
+
if (Platform.OS !== "android") return
|
|
355
|
+
setNativeHttp(async (input, init) => {
|
|
356
|
+
if (typeof input !== "string" || (init?.body != null && typeof init.body !== "string")) {
|
|
357
|
+
return globalThis.fetch(input, init)
|
|
358
|
+
}
|
|
359
|
+
const headers = Object.fromEntries(new Headers(init?.headers).entries())
|
|
360
|
+
const result = await CrustModule.nativeHttpRequest(
|
|
361
|
+
init?.method ?? "GET",
|
|
362
|
+
input,
|
|
363
|
+
headers,
|
|
364
|
+
(init?.body as string | undefined) ?? null,
|
|
365
|
+
)
|
|
366
|
+
return new Response(nativeHttpResponseBody(result.status, result.body), {
|
|
367
|
+
status: result.status,
|
|
368
|
+
statusText: result.statusText,
|
|
369
|
+
headers: result.headers,
|
|
370
|
+
})
|
|
371
|
+
})
|
|
325
372
|
}
|
|
326
373
|
|
|
327
374
|
function clearRuntimeEventSubscriptions(): void {
|
|
@@ -375,6 +422,12 @@ function construct(): void {
|
|
|
375
422
|
// LC3 at 16 kHz with the frame size the encoder emits.
|
|
376
423
|
audio: {codec: "lc3", sampleRate: 16000, frameSizeBytes: frameSizeBytes()},
|
|
377
424
|
auth,
|
|
425
|
+
timers: {
|
|
426
|
+
setTimeout: (callback, delayMs) => BgTimer.setTimeout(callback, delayMs),
|
|
427
|
+
clearTimeout: (handle) => BgTimer.clearTimeout(handle as number),
|
|
428
|
+
setInterval: (callback, intervalMs) => BgTimer.setInterval(callback, intervalMs),
|
|
429
|
+
clearInterval: (handle) => BgTimer.clearInterval(handle as number),
|
|
430
|
+
},
|
|
378
431
|
logger: cloudLogger,
|
|
379
432
|
})
|
|
380
433
|
|
|
@@ -399,6 +452,7 @@ function construct(): void {
|
|
|
399
452
|
if (c !== client) return
|
|
400
453
|
connected = true
|
|
401
454
|
console.log(`${LOG_TAG}: runtime connected`)
|
|
455
|
+
console.log(`${LOG_TAG}: debug: ws-session-debug connected`)
|
|
402
456
|
// Cloud is up — cancel any pending persistent-failure alarm and re-arm for the
|
|
403
457
|
// next outage.
|
|
404
458
|
clearPersistentFailureAlarm()
|
|
@@ -415,6 +469,9 @@ function construct(): void {
|
|
|
415
469
|
if (c !== client) return
|
|
416
470
|
connected = false
|
|
417
471
|
console.log(`${LOG_TAG}: runtime disconnected (${info.reason})`)
|
|
472
|
+
console.log(
|
|
473
|
+
`${LOG_TAG}: debug: ws-session-debug disconnected reason=${info.reason} willStayDown=${/superseded by newer session/i.test(info.reason)}`,
|
|
474
|
+
)
|
|
418
475
|
// Arm the persistent-failure alarm once; if we're still down when it fires, raise
|
|
419
476
|
// the notification. A reconnect within the window cancels it (onConnected above).
|
|
420
477
|
if (!persistentFailureTimer && !persistentFailureNotified) {
|
|
@@ -497,6 +554,34 @@ export const cloudClientService = {
|
|
|
497
554
|
return c.core.miniapps.getRegistry()
|
|
498
555
|
},
|
|
499
556
|
|
|
557
|
+
/**
|
|
558
|
+
* Resolve the stable Core-owned Mentra user id.
|
|
559
|
+
*
|
|
560
|
+
* Storage must use this identity rather than the short-lived Core access
|
|
561
|
+
* token. A previously verified id is restored from MMKV so local miniapps
|
|
562
|
+
* remain available after an offline process restart. On first sign-in,
|
|
563
|
+
* callers join the auth module's single-flight exchange/refresh instead of
|
|
564
|
+
* falling back to an anonymous namespace.
|
|
565
|
+
*/
|
|
566
|
+
async resolveMentraUserId(): Promise<string> {
|
|
567
|
+
return localMiniappUserIdentity.resolve(async () => {
|
|
568
|
+
if (!client) this.init()
|
|
569
|
+
const c = client
|
|
570
|
+
if (!c) throw new Error("cloud client not initialized")
|
|
571
|
+
|
|
572
|
+
await c.auth.getCoreToken()
|
|
573
|
+
if (c !== client) throw new Error("cloud client changed while resolving user identity")
|
|
574
|
+
return c.auth.identity.mentraUserId
|
|
575
|
+
})
|
|
576
|
+
},
|
|
577
|
+
|
|
578
|
+
/** Read the stable Mentra user id after async identity resolution. */
|
|
579
|
+
getMentraUserId(): string {
|
|
580
|
+
const userId = localMiniappUserIdentity.get()
|
|
581
|
+
if (!userId) throw new Error("Mentra user identity is unavailable")
|
|
582
|
+
return userId
|
|
583
|
+
},
|
|
584
|
+
|
|
500
585
|
async getMiniappAuthToken(
|
|
501
586
|
packageName: string,
|
|
502
587
|
opts?: {minTtlMs?: number; devAttestation?: string},
|
|
@@ -528,6 +613,11 @@ export const cloudClientService = {
|
|
|
528
613
|
clearPersistentFailureAlarm()
|
|
529
614
|
const wasConnected = connected
|
|
530
615
|
client = null
|
|
616
|
+
// stop() is part of the logout lifecycle and runs before the host emits
|
|
617
|
+
// SIGNED_OUT. Remove the persisted namespace owner here so a later account
|
|
618
|
+
// can never inherit it. A force-stop/process kill does not call stop(), so
|
|
619
|
+
// the cached id still survives the offline cold-start case.
|
|
620
|
+
localMiniappUserIdentity.forget()
|
|
531
621
|
localDevRuntimeToken = null
|
|
532
622
|
connected = false
|
|
533
623
|
resetRuntimeStatus()
|
|
@@ -660,6 +750,11 @@ export const cloudClientService = {
|
|
|
660
750
|
return getCoreClient().reports.complete(...args)
|
|
661
751
|
},
|
|
662
752
|
},
|
|
753
|
+
supportProfile: {
|
|
754
|
+
update(...args: Parameters<CloudCore["supportProfile"]["update"]>) {
|
|
755
|
+
return getCoreClient().supportProfile.update(...args)
|
|
756
|
+
},
|
|
757
|
+
},
|
|
663
758
|
},
|
|
664
759
|
|
|
665
760
|
hasAudioSubscriptions(): boolean {
|
|
@@ -201,17 +201,17 @@ export function startDeviceEventRouter(): void {
|
|
|
201
201
|
localMiniappRuntime.forwardEvent("head_up", {position: event.up ? "up" : "down", timestamp: Date.now()})
|
|
202
202
|
}),
|
|
203
203
|
)
|
|
204
|
-
// On-device STT transcripts → local miniapps
|
|
205
|
-
// active
|
|
206
|
-
//
|
|
207
|
-
//
|
|
204
|
+
// On-device STT transcripts → local miniapps only while the local engine is
|
|
205
|
+
// active (cloud fallback or an explicit forceLocal subscription). The runtime
|
|
206
|
+
// filters mixed local/cloud delivery per miniapp to avoid double-delivery.
|
|
207
|
+
// forwardEvent is subscriber-gated — a no-op when no miniapp listens.
|
|
208
208
|
// (Was MantleManager.handle_local_transcription; its offline-captions display branch
|
|
209
209
|
// went away with the pseudo captions renderer.)
|
|
210
210
|
subs.push(
|
|
211
211
|
BluetoothSdk.addListener("local_transcription", (event) => {
|
|
212
212
|
if (!localSttFallbackCoordinator.isActive()) return
|
|
213
213
|
const lang = event.transcribeLanguage ?? localSttFallbackCoordinator.getActiveLanguage() ?? "en-US"
|
|
214
|
-
localMiniappRuntime.forwardEvent(`transcription:${lang}`, event)
|
|
214
|
+
localMiniappRuntime.forwardEvent(`transcription:${lang}`, event, "local")
|
|
215
215
|
}),
|
|
216
216
|
)
|
|
217
217
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ForegroundLocationPermissionResponse {
|
|
2
|
+
status: string
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface ForegroundLocationPermissionClient {
|
|
6
|
+
getForegroundPermissionsAsync(): Promise<ForegroundLocationPermissionResponse>
|
|
7
|
+
requestForegroundPermissionsAsync(): Promise<ForegroundLocationPermissionResponse>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve foreground location permission without invoking an Activity-bound
|
|
12
|
+
* Android permission request while the app is backgrounded.
|
|
13
|
+
*/
|
|
14
|
+
export async function resolveForegroundLocationPermission(
|
|
15
|
+
client: ForegroundLocationPermissionClient,
|
|
16
|
+
getAppState: () => string,
|
|
17
|
+
): Promise<ForegroundLocationPermissionResponse> {
|
|
18
|
+
const current = await client.getForegroundPermissionsAsync()
|
|
19
|
+
if (current.status === "granted" || getAppState() !== "active") {
|
|
20
|
+
return current
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return client.requestForegroundPermissionsAsync()
|
|
24
|
+
}
|
|
@@ -19,6 +19,7 @@ import {useSettingsStore, PAIRING_IDENTITY_KEYS} from "../stores/settings"
|
|
|
19
19
|
import {useGlassesStore} from "../stores/glasses"
|
|
20
20
|
import {createDebouncedPatchFlusher} from "../utils/debouncedPatch"
|
|
21
21
|
import {isGlassesConnected} from "./GlassesReadiness"
|
|
22
|
+
import micStateCoordinator from "./MicStateCoordinator"
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Change-pushes are debounced (300ms) and merged so a burst of setSetting
|
|
@@ -28,7 +29,10 @@ import {isGlassesConnected} from "./GlassesReadiness"
|
|
|
28
29
|
const flushBluetoothSettingsPatch = createDebouncedPatchFlusher<Record<string, unknown>>((patch) => {
|
|
29
30
|
// Settings can change while the glasses are disconnected — a native
|
|
30
31
|
// rejection must not surface as an unhandled promise rejection.
|
|
31
|
-
|
|
32
|
+
// Apply mic overrides at flush time, not enqueue time: raw PCM can stop
|
|
33
|
+
// during the debounce window and a captured VAD=false would then be stale.
|
|
34
|
+
const runtimePatch = micStateCoordinator.applyRuntimeOverrides(patch)
|
|
35
|
+
void Promise.resolve(BluetoothSdk.updateBluetoothSettings(runtimePatch)).catch((error) => {
|
|
32
36
|
console.warn("GlassesSettingsSync: updateBluetoothSettings failed:", error)
|
|
33
37
|
})
|
|
34
38
|
}, 300)
|
|
@@ -79,7 +83,8 @@ export async function pushAllBluetoothSettings(): Promise<void> {
|
|
|
79
83
|
// Returns the native write promise so callers can await the seed before the
|
|
80
84
|
// connect handshake replays settings to the glasses (otherwise the handshake
|
|
81
85
|
// can race ahead and replay stale native settings).
|
|
82
|
-
|
|
86
|
+
const settings = useSettingsStore.getState().getBluetoothSettings()
|
|
87
|
+
await BluetoothSdk.updateBluetoothSettings(micStateCoordinator.applyRuntimeOverrides(settings))
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
/**
|
|
@@ -91,7 +96,8 @@ export async function pushAllBluetoothSettings(): Promise<void> {
|
|
|
91
96
|
* with pre-promotion empties (wiping the pairing it had just made).
|
|
92
97
|
*/
|
|
93
98
|
export async function pushDeviceSettingsOnConnect(): Promise<void> {
|
|
94
|
-
|
|
99
|
+
const settings = stripPairingIdentity(useSettingsStore.getState().getBluetoothSettings())
|
|
100
|
+
await BluetoothSdk.updateBluetoothSettings(micStateCoordinator.applyRuntimeOverrides(settings))
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
export function startGlassesSettingsSync(): void {
|
|
@@ -12,21 +12,24 @@
|
|
|
12
12
|
* Started by `engine.start()`. Idempotent.
|
|
13
13
|
*/
|
|
14
14
|
import BluetoothSdk from "@mentra/bluetooth-sdk/internal"
|
|
15
|
+
import type {GlassesStatus} from "@mentra/bluetooth-sdk/internal"
|
|
15
16
|
import {useCoreStore} from "../stores/core"
|
|
16
17
|
import {useGlassesStore} from "../stores/glasses"
|
|
17
|
-
import localMiniappRuntime from "./LocalMiniappRuntime"
|
|
18
18
|
|
|
19
19
|
let unsubs: Array<() => void> = []
|
|
20
20
|
let projectionRunId = 0
|
|
21
|
+
let glassesStatusForwarder: ((status: Partial<GlassesStatus>) => void) | null = null
|
|
22
|
+
let hydrationPromise: Promise<void> | null = null
|
|
21
23
|
|
|
22
|
-
export function startGlassesStatusProjection(): void {
|
|
23
|
-
if (
|
|
24
|
+
export function startGlassesStatusProjection(forwarder?: (status: Partial<GlassesStatus>) => void): Promise<void> {
|
|
25
|
+
if (forwarder) glassesStatusForwarder = forwarder
|
|
26
|
+
if (unsubs.length) return hydrationPromise ?? Promise.resolve()
|
|
24
27
|
|
|
25
28
|
const runId = ++projectionRunId
|
|
26
29
|
let bluetoothEventSeen = false
|
|
27
30
|
let glassesEventSeen = false
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
const bluetoothHydration = BluetoothSdk.getBluetoothStatus()
|
|
30
33
|
.then((status) => {
|
|
31
34
|
if (runId !== projectionRunId || bluetoothEventSeen) return
|
|
32
35
|
useCoreStore.getState().setCoreInfo(status)
|
|
@@ -35,7 +38,7 @@ export function startGlassesStatusProjection(): void {
|
|
|
35
38
|
console.warn("GlassesStatusProjection: getBluetoothStatus failed", error)
|
|
36
39
|
})
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
const glassesHydration = BluetoothSdk.getGlassesStatus()
|
|
39
42
|
.then((status) => {
|
|
40
43
|
if (runId !== projectionRunId || glassesEventSeen) return
|
|
41
44
|
useGlassesStore.getState().setGlassesInfo(status)
|
|
@@ -52,22 +55,27 @@ export function startGlassesStatusProjection(): void {
|
|
|
52
55
|
}),
|
|
53
56
|
)
|
|
54
57
|
|
|
55
|
-
// Glasses status -> glasses store (+
|
|
56
|
-
// OTA-available flag on disconnect).
|
|
58
|
+
// Glasses status -> glasses store (+ optional full-runtime forwarding to local
|
|
59
|
+
// miniapps; clear any stale OTA-available flag on disconnect).
|
|
57
60
|
unsubs.push(
|
|
58
61
|
BluetoothSdk.onGlassesStatus((changed) => {
|
|
59
62
|
glassesEventSeen = true
|
|
60
63
|
useGlassesStore.getState().setGlassesInfo(changed)
|
|
61
|
-
|
|
64
|
+
glassesStatusForwarder?.(changed)
|
|
62
65
|
if (changed.connection?.state === "disconnected") {
|
|
63
66
|
useGlassesStore.getState().setOtaUpdateAvailable(null)
|
|
64
67
|
}
|
|
65
68
|
}),
|
|
66
69
|
)
|
|
70
|
+
|
|
71
|
+
hydrationPromise = Promise.allSettled([bluetoothHydration, glassesHydration]).then(() => undefined)
|
|
72
|
+
return hydrationPromise
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
export function stopGlassesStatusProjection(): void {
|
|
70
76
|
projectionRunId++
|
|
71
77
|
unsubs.forEach((unsub) => unsub())
|
|
72
78
|
unsubs = []
|
|
79
|
+
glassesStatusForwarder = null
|
|
80
|
+
hydrationPromise = null
|
|
73
81
|
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import BluetoothSdk, {isEnabledHotspotStatus, MentraOtaServer} from "@mentra/bluetooth-sdk/internal"
|
|
2
|
+
// One coordinator selects the native Android or iOS staging implementation at runtime.
|
|
3
|
+
// eslint-disable-next-line react-native/split-platform-components
|
|
4
|
+
import {PermissionsAndroid, Platform} from "react-native"
|
|
5
|
+
import type {OtaCheckCurrentGlassesResult} from "./OtaUpdateCheckService"
|
|
6
|
+
import {
|
|
7
|
+
cleanupArtifacts,
|
|
8
|
+
OtaArtifactError,
|
|
9
|
+
planArtifacts,
|
|
10
|
+
prepareArtifacts,
|
|
11
|
+
rewriteManifestForLocalServer,
|
|
12
|
+
type OtaArtifactDownloadProgress,
|
|
13
|
+
type PreparedOtaArtifact,
|
|
14
|
+
} from "./OtaArtifactDownloader"
|
|
15
|
+
import {disableHotspotWithRetry} from "./HotspotShutdown"
|
|
16
|
+
import {localNetworkTransport} from "./asg/localNetworkTransport"
|
|
17
|
+
|
|
18
|
+
export type HotspotOtaPhase = "idle" | "downloading" | "starting_hotspot" | "joining_hotspot" | "serving"
|
|
19
|
+
|
|
20
|
+
export type HotspotOtaProgress = {
|
|
21
|
+
phase: HotspotOtaPhase
|
|
22
|
+
artifact?: OtaArtifactDownloadProgress
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type HotspotOtaErrorCode =
|
|
26
|
+
| "hotspot_wifi_permission_denied"
|
|
27
|
+
| "hotspot_start_failed"
|
|
28
|
+
| "hotspot_join_failed"
|
|
29
|
+
| "hotspot_server_failed"
|
|
30
|
+
|
|
31
|
+
export class HotspotOtaTransportError extends Error {
|
|
32
|
+
constructor(
|
|
33
|
+
public readonly code: HotspotOtaErrorCode,
|
|
34
|
+
message: string,
|
|
35
|
+
) {
|
|
36
|
+
super(message)
|
|
37
|
+
this.name = "HotspotOtaTransportError"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Owns the single phone-side endpoint and network lease for one hotspot OTA attempt. */
|
|
42
|
+
class HotspotOtaTransport {
|
|
43
|
+
private active = false
|
|
44
|
+
private prepared: PreparedOtaArtifact[] = []
|
|
45
|
+
private hotspotRequested = false
|
|
46
|
+
private localNetworkConnected = false
|
|
47
|
+
private serverStarted = false
|
|
48
|
+
private teardownPromise: Promise<void> | null = null
|
|
49
|
+
|
|
50
|
+
async prepare(
|
|
51
|
+
checkResult: OtaCheckCurrentGlassesResult,
|
|
52
|
+
onProgress?: (progress: HotspotOtaProgress) => void,
|
|
53
|
+
): Promise<string> {
|
|
54
|
+
if (this.teardownPromise) await this.teardownPromise
|
|
55
|
+
if (this.active) {
|
|
56
|
+
throw new Error("A hotspot OTA transport is already active")
|
|
57
|
+
}
|
|
58
|
+
if (!checkResult.manifestBody) {
|
|
59
|
+
throw new Error("The selected OTA check has no manifest body")
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let phase: HotspotOtaPhase = "downloading"
|
|
63
|
+
try {
|
|
64
|
+
await this.ensureAndroidWifiPermission()
|
|
65
|
+
onProgress?.({phase: "downloading"})
|
|
66
|
+
this.prepared = await prepareArtifacts(
|
|
67
|
+
planArtifacts(checkResult),
|
|
68
|
+
(artifact) => onProgress?.({phase: "downloading", artifact}),
|
|
69
|
+
Platform.OS === "ios" ? this.downloadIosArtifact : undefined,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
phase = "starting_hotspot"
|
|
73
|
+
onProgress?.({phase: "starting_hotspot"})
|
|
74
|
+
this.hotspotRequested = true
|
|
75
|
+
const hotspot = await BluetoothSdk.setHotspotState(true)
|
|
76
|
+
if (!isEnabledHotspotStatus(hotspot)) {
|
|
77
|
+
throw new HotspotOtaTransportError("hotspot_start_failed", "Mentra Live did not return hotspot credentials")
|
|
78
|
+
}
|
|
79
|
+
phase = "joining_hotspot"
|
|
80
|
+
onProgress?.({phase: "joining_hotspot"})
|
|
81
|
+
const scopedAddress = await localNetworkTransport.connect(hotspot.ssid, hotspot.password)
|
|
82
|
+
this.localNetworkConnected = true
|
|
83
|
+
let localAddress = scopedAddress
|
|
84
|
+
if (Platform.OS === "ios") {
|
|
85
|
+
try {
|
|
86
|
+
localAddress = await MentraOtaServer.waitForWifiAddress(hotspot.localIp, 15_000)
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw new HotspotOtaTransportError(
|
|
89
|
+
"hotspot_join_failed",
|
|
90
|
+
error instanceof Error ? error.message : String(error),
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const artifactPaths = Object.fromEntries(this.prepared.map((artifact) => [artifact.sha256, artifact.filePath]))
|
|
95
|
+
// Start exactly one native listener to learn its selected port, then atomically replace
|
|
96
|
+
// the placeholder with the immutable rewritten manifest before ota_start is sent.
|
|
97
|
+
const server = await MentraOtaServer.startOtaServer("{}", artifactPaths, localAddress)
|
|
98
|
+
this.serverStarted = true
|
|
99
|
+
const manifest = rewriteManifestForLocalServer(checkResult.manifestBody, this.prepared, server.baseUrl)
|
|
100
|
+
const published = await MentraOtaServer.startOtaServer(manifest, artifactPaths, server.host)
|
|
101
|
+
if (published.manifestUrl !== server.manifestUrl) {
|
|
102
|
+
throw new Error("Local OTA server endpoint changed while publishing the manifest")
|
|
103
|
+
}
|
|
104
|
+
this.active = true
|
|
105
|
+
onProgress?.({phase: "serving"})
|
|
106
|
+
return published.manifestUrl
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const joined = this.localNetworkConnected
|
|
109
|
+
await this.teardown()
|
|
110
|
+
if (error instanceof OtaArtifactError || error instanceof HotspotOtaTransportError) {
|
|
111
|
+
throw error
|
|
112
|
+
}
|
|
113
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
114
|
+
if (phase === "starting_hotspot") {
|
|
115
|
+
throw new HotspotOtaTransportError("hotspot_start_failed", message)
|
|
116
|
+
}
|
|
117
|
+
if (phase === "joining_hotspot" && !joined) {
|
|
118
|
+
throw new HotspotOtaTransportError("hotspot_join_failed", message)
|
|
119
|
+
}
|
|
120
|
+
throw new HotspotOtaTransportError("hotspot_server_failed", message)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Android 13+ requires the nearby-WiFi runtime grant before WifiNetworkSpecifier is usable. */
|
|
125
|
+
private async ensureAndroidWifiPermission(): Promise<void> {
|
|
126
|
+
if (Platform.OS !== "android") return
|
|
127
|
+
|
|
128
|
+
const permission = PermissionsAndroid.PERMISSIONS.NEARBY_WIFI_DEVICES
|
|
129
|
+
if (await PermissionsAndroid.check(permission)) return
|
|
130
|
+
const result = await PermissionsAndroid.request(permission)
|
|
131
|
+
if (result !== PermissionsAndroid.RESULTS.GRANTED) {
|
|
132
|
+
throw new HotspotOtaTransportError(
|
|
133
|
+
"hotspot_wifi_permission_denied",
|
|
134
|
+
"Nearby devices permission is required to connect to the glasses hotspot",
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
private downloadIosArtifact = async (
|
|
140
|
+
entry: {url: string},
|
|
141
|
+
destination: string,
|
|
142
|
+
onProgress?: (bytesWritten: number, contentLength: number) => void,
|
|
143
|
+
): Promise<{statusCode: number}> => {
|
|
144
|
+
const subscription = MentraOtaServer.addListener("artifactDownloadProgress", (event) => {
|
|
145
|
+
if (event.destination === destination) onProgress?.(event.bytesWritten, event.contentLength)
|
|
146
|
+
})
|
|
147
|
+
try {
|
|
148
|
+
return await MentraOtaServer.downloadArtifact(entry.url, destination)
|
|
149
|
+
} finally {
|
|
150
|
+
subscription.remove()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async teardown(): Promise<void> {
|
|
155
|
+
if (this.teardownPromise) return this.teardownPromise
|
|
156
|
+
this.teardownPromise = (async () => {
|
|
157
|
+
if (this.hotspotRequested) {
|
|
158
|
+
const hotspotStopped = await disableHotspotWithRetry(() => BluetoothSdk.setHotspotState(false), {
|
|
159
|
+
// A completed APK step has just replaced ASG. Let the new command path
|
|
160
|
+
// settle before asking it to tear down the SystemUI-owned access point.
|
|
161
|
+
initialDelayMs: this.serverStarted ? 750 : 0,
|
|
162
|
+
})
|
|
163
|
+
if (!hotspotStopped) {
|
|
164
|
+
console.warn("[OTA_PROGRESS] glasses hotspot shutdown was not confirmed after bounded retries")
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (this.serverStarted) await MentraOtaServer.stopOtaServer().catch(() => {})
|
|
168
|
+
if (this.localNetworkConnected) await localNetworkTransport.disconnect().catch(() => {})
|
|
169
|
+
await cleanupArtifacts().catch(() => {})
|
|
170
|
+
this.prepared = []
|
|
171
|
+
this.active = false
|
|
172
|
+
this.serverStarted = false
|
|
173
|
+
this.localNetworkConnected = false
|
|
174
|
+
this.hotspotRequested = false
|
|
175
|
+
})()
|
|
176
|
+
try {
|
|
177
|
+
await this.teardownPromise
|
|
178
|
+
} finally {
|
|
179
|
+
this.teardownPromise = null
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const hotspotOtaTransport = new HotspotOtaTransport()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
type HotspotState = {state: "disabled" | "enabled"}
|
|
2
|
+
|
|
3
|
+
type HotspotShutdownOptions = {
|
|
4
|
+
attempts?: number
|
|
5
|
+
initialDelayMs?: number
|
|
6
|
+
retryDelayMs?: number
|
|
7
|
+
sleep?: (delayMs: number) => Promise<void>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const sleep = (delayMs: number) => new Promise<void>((resolve) => setTimeout(resolve, delayMs))
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Disable a glasses hotspot with a bounded retry. ASG APK replacement keeps the
|
|
14
|
+
* BLE link alive through BES, but the first command after the new ASG process
|
|
15
|
+
* appears can still land while its command path is settling.
|
|
16
|
+
*/
|
|
17
|
+
export async function disableHotspotWithRetry(
|
|
18
|
+
requestDisabled: () => Promise<HotspotState>,
|
|
19
|
+
options: HotspotShutdownOptions = {},
|
|
20
|
+
): Promise<boolean> {
|
|
21
|
+
const attempts = Math.max(1, options.attempts ?? 2)
|
|
22
|
+
const initialDelayMs = Math.max(0, options.initialDelayMs ?? 0)
|
|
23
|
+
const retryDelayMs = Math.max(0, options.retryDelayMs ?? 500)
|
|
24
|
+
const wait = options.sleep ?? sleep
|
|
25
|
+
|
|
26
|
+
if (initialDelayMs > 0) await wait(initialDelayMs)
|
|
27
|
+
|
|
28
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
29
|
+
try {
|
|
30
|
+
const status = await requestDisabled()
|
|
31
|
+
if (status.state === "disabled") return true
|
|
32
|
+
} catch {
|
|
33
|
+
// A lost command is indistinguishable from a response timeout here. Retry
|
|
34
|
+
// while the phone still owns the hotspot network and local server.
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (attempt < attempts && retryDelayMs > 0) await wait(retryDelayMs)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return false
|
|
41
|
+
}
|