@mentra/engine 3.2.0-dev.226 → 3.2.0-dev.232
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/build/generated/releaseMetadata.js +5 -5
- package/build/generated/releaseMetadata.js.map +1 -1
- package/build/services/AcsMeetingService.d.ts +15 -7
- package/build/services/AcsMeetingService.d.ts.map +1 -1
- package/build/services/AcsMeetingService.js +32 -9
- package/build/services/AcsMeetingService.js.map +1 -1
- package/build/services/GlassesHotspotLease.d.ts +2 -0
- package/build/services/GlassesHotspotLease.d.ts.map +1 -0
- package/build/services/GlassesHotspotLease.js +13 -0
- package/build/services/GlassesHotspotLease.js.map +1 -0
- package/build/services/LocalMiniappRuntime.d.ts +2 -0
- package/build/services/LocalMiniappRuntime.d.ts.map +1 -1
- package/build/services/LocalMiniappRuntime.js +35 -11
- package/build/services/LocalMiniappRuntime.js.map +1 -1
- package/build/services/ManagedWebRtcRelay.d.ts +87 -0
- package/build/services/ManagedWebRtcRelay.d.ts.map +1 -0
- package/build/services/ManagedWebRtcRelay.js +239 -0
- package/build/services/ManagedWebRtcRelay.js.map +1 -0
- package/build/services/PhoneStreamCoordinator.d.ts +5 -0
- package/build/services/PhoneStreamCoordinator.d.ts.map +1 -1
- package/build/services/PhoneStreamCoordinator.js +131 -68
- package/build/services/PhoneStreamCoordinator.js.map +1 -1
- package/build/services/SoftapCallTransport.d.ts +5 -1
- package/build/services/SoftapCallTransport.d.ts.map +1 -1
- package/build/services/SoftapCallTransport.js +23 -5
- package/build/services/SoftapCallTransport.js.map +1 -1
- package/package.json +8 -7
- package/src/generated/releaseMetadata.ts +5 -5
- package/src/services/AcsMeetingService.ts +38 -9
- package/src/services/GlassesHotspotLease.ts +11 -0
- package/src/services/LocalMiniappRuntime.ts +47 -18
- package/src/services/ManagedWebRtcRelay.ts +278 -0
- package/src/services/PhoneStreamCoordinator.ts +152 -70
- package/src/services/SoftapCallTransport.ts +28 -7
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* This runtime handles request dispatch, stream fan-out, and lifecycle
|
|
9
9
|
* management (connect/disconnect/ping).
|
|
10
10
|
*/
|
|
11
|
+
import { acquireGlassesHotspot } from "./GlassesHotspotLease";
|
|
11
12
|
import { AppState, Linking } from "react-native";
|
|
12
13
|
import Share from "react-native-share";
|
|
13
14
|
import * as Battery from "expo-battery";
|
|
@@ -46,7 +47,7 @@ import { miniappLauncher } from "./MiniappLauncher";
|
|
|
46
47
|
import { ISLAND_SETTINGS_KEYS, } from "../runtime/config";
|
|
47
48
|
import { getAnalytics, getMiniappConfiguration, getUiSeams, isFeatureEnabled } from "../runtime/bootstrap";
|
|
48
49
|
import { invokeScanQrSeam } from "../runtime/scanQrSeam";
|
|
49
|
-
import { normalizeStreamAudioConfig, normalizeStreamVideoConfig, resolveCaptureAudio } from "../runtime/streamConfig";
|
|
50
|
+
import { normalizeStreamAudioConfig, normalizeStreamVideoConfig, normalizeCaptureAudio, resolveCaptureAudio, } from "../runtime/streamConfig";
|
|
50
51
|
import { toLanguageHint } from "@mentra/cloud-protocol/languages";
|
|
51
52
|
import { buildMiniappManifestSnapshot } from "../utils/miniappDiagnostics";
|
|
52
53
|
import ttsModelManager from "./TTSModelManager";
|
|
@@ -774,13 +775,14 @@ class LocalMiniappRuntime {
|
|
|
774
775
|
// remain active for another subscriber; a rejected FOV release then stops
|
|
775
776
|
// renewing the departed owner's lease and ASG restores it once idle.
|
|
776
777
|
const cameraCleanup = Promise.allSettled([
|
|
778
|
+
this.leaveMeetingForApp(packageName),
|
|
777
779
|
phonePhotoCoordinator.stopWarmUpForApp(packageName),
|
|
778
780
|
phoneStreamCoordinator.stop(packageName),
|
|
779
781
|
phoneVideoCoordinator.stopForApp(packageName),
|
|
780
782
|
]).then((results) => {
|
|
781
783
|
for (const result of results) {
|
|
782
784
|
if (result.status === "rejected") {
|
|
783
|
-
console.warn(`${LOG_TAG}: failed to stop
|
|
785
|
+
console.warn(`${LOG_TAG}: failed to stop miniapp resource for ${packageName} on unregister`, result.reason);
|
|
784
786
|
}
|
|
785
787
|
}
|
|
786
788
|
});
|
|
@@ -808,9 +810,6 @@ class LocalMiniappRuntime {
|
|
|
808
810
|
// Tear down this app's blob state: abort in-flight uploads + close readers
|
|
809
811
|
// so a crashed/closed miniapp doesn't leak partial files or file handles.
|
|
810
812
|
this.blobStore.onAppGone(packageName);
|
|
811
|
-
void acsMeetingService.leaveIfOwner(packageName).catch((error) => {
|
|
812
|
-
console.warn(`${LOG_TAG}: failed to leave ACS meeting for ${packageName} on unregister`, error);
|
|
813
|
-
});
|
|
814
813
|
// Detach the per-app nav event forwarder but leave the native nav session
|
|
815
814
|
// running. The user may have just closed the mini-app UI and will reopen
|
|
816
815
|
// it; stopping the session here would kill an active trip mid-route.
|
|
@@ -3083,13 +3082,18 @@ class LocalMiniappRuntime {
|
|
|
3083
3082
|
return;
|
|
3084
3083
|
}
|
|
3085
3084
|
try {
|
|
3085
|
+
if (payload.ingest === "whip" && !(await permissions.check(PermissionFeatures.LOCAL_WIFI))) {
|
|
3086
|
+
if (!(await permissions.request(PermissionFeatures.LOCAL_WIFI))) {
|
|
3087
|
+
throw new Error("Nearby devices permission is required to join the glasses hotspot");
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3086
3090
|
const result = await streaming.startManaged(packageName, {
|
|
3087
3091
|
restreamDestinations: payload.restreamDestinations,
|
|
3088
3092
|
video: normalizeStreamVideoConfig(payload.video),
|
|
3089
3093
|
audio: normalizeStreamAudioConfig(payload.audio),
|
|
3090
3094
|
sound: payload.sound,
|
|
3091
3095
|
ingest: payload.ingest,
|
|
3092
|
-
captureAudio:
|
|
3096
|
+
captureAudio: normalizeCaptureAudio(payload.captureAudio),
|
|
3093
3097
|
});
|
|
3094
3098
|
this.sendResult(packageName, requestId, true, result);
|
|
3095
3099
|
}
|
|
@@ -3107,12 +3111,26 @@ class LocalMiniappRuntime {
|
|
|
3107
3111
|
}
|
|
3108
3112
|
ensureMeetingStateBridge() {
|
|
3109
3113
|
acsMeetingService.setStateHandler((owner, state) => {
|
|
3114
|
+
const attempt = this.softapAttempt;
|
|
3115
|
+
// While a replacement waits for cleanup, native events still describe its predecessor.
|
|
3116
|
+
if (attempt?.packageName === owner && !attempt.ownsResources)
|
|
3117
|
+
return;
|
|
3110
3118
|
this.sendToMiniapp(owner, {
|
|
3111
3119
|
type: MiniappResponseType.MEETING_STATE,
|
|
3112
3120
|
...state,
|
|
3113
3121
|
});
|
|
3114
3122
|
});
|
|
3115
3123
|
}
|
|
3124
|
+
/** Startup owns the hotspot before ACS has an owner. Closing the app must retire both. */
|
|
3125
|
+
async leaveMeetingForApp(packageName) {
|
|
3126
|
+
const attempt = this.softapAttempt;
|
|
3127
|
+
if (attempt?.packageName === packageName) {
|
|
3128
|
+
await this.retireSoftapAttempt({ attempt });
|
|
3129
|
+
}
|
|
3130
|
+
else {
|
|
3131
|
+
await acsMeetingService.leaveIfOwner(packageName);
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3116
3134
|
async handleMeetingJoin(packageName, payload, requestId) {
|
|
3117
3135
|
this.ensureMeetingStateBridge();
|
|
3118
3136
|
// A meeting puts the glasses camera and mic in front of remote strangers; it
|
|
@@ -3319,7 +3337,8 @@ class LocalMiniappRuntime {
|
|
|
3319
3337
|
return;
|
|
3320
3338
|
}
|
|
3321
3339
|
attempt.progress = progress;
|
|
3322
|
-
|
|
3340
|
+
// A reservation waiting behind another call must not inherit that call's connected state.
|
|
3341
|
+
const current = attempt.transport ? acsMeetingService.getState() : { state: "connecting", muted: false };
|
|
3323
3342
|
this.sendToMiniapp(attempt.packageName, {
|
|
3324
3343
|
type: MiniappResponseType.MEETING_STATE,
|
|
3325
3344
|
...current,
|
|
@@ -3347,6 +3366,7 @@ class LocalMiniappRuntime {
|
|
|
3347
3366
|
async runSoftapAttempt(attempt, previous, args) {
|
|
3348
3367
|
const packageName = attempt.packageName;
|
|
3349
3368
|
if (previous) {
|
|
3369
|
+
this.narrateSoftapPreflight(attempt, "Finishing the previous call’s cleanup…");
|
|
3350
3370
|
softapTrace("softap_join_superseding", {
|
|
3351
3371
|
previousAttempt: previous.id,
|
|
3352
3372
|
previousAgeMs: Date.now() - previous.startedAt,
|
|
@@ -3371,6 +3391,7 @@ class LocalMiniappRuntime {
|
|
|
3371
3391
|
softapTraceFailure("softap_join_refused", { packageName, reason: cleanupError });
|
|
3372
3392
|
throw new Error(`Previous call cleanup failed: ${cleanupError}. Power-cycle the glasses hotspot and try again.`);
|
|
3373
3393
|
}
|
|
3394
|
+
attempt.releaseHotspot = acquireGlassesHotspot();
|
|
3374
3395
|
attempt.ownsResources = true;
|
|
3375
3396
|
this.narrateSoftapPreflight(attempt, "Checking the Nearby devices permission…");
|
|
3376
3397
|
const permissionStartedAt = Date.now();
|
|
@@ -3401,12 +3422,13 @@ class LocalMiniappRuntime {
|
|
|
3401
3422
|
setHotspotState: async (enabled) => {
|
|
3402
3423
|
const status = await BluetoothSdk.setHotspotState(enabled);
|
|
3403
3424
|
if (status.state === "enabled") {
|
|
3404
|
-
return { state: status.state, ssid: status.ssid, password: status.password };
|
|
3425
|
+
return { state: status.state, ssid: status.ssid, password: status.password, localIp: status.localIp };
|
|
3405
3426
|
}
|
|
3406
3427
|
return { state: status.state };
|
|
3407
3428
|
},
|
|
3408
|
-
joinScopedNetwork: (ssid, passphrase) => acsMeetingService.joinScopedNetwork(ssid, passphrase),
|
|
3429
|
+
joinScopedNetwork: (ssid, passphrase, gateway) => acsMeetingService.joinScopedNetwork(ssid, passphrase, gateway),
|
|
3409
3430
|
leaveScopedNetwork: () => acsMeetingService.leaveScopedNetwork(),
|
|
3431
|
+
cancelScopedNetworkJoin: () => acsMeetingService.cancelScopedNetworkJoin(),
|
|
3410
3432
|
probeGateway: async () => {
|
|
3411
3433
|
const verdict = await acsMeetingService.probeScopedGateway();
|
|
3412
3434
|
// No native probe means no verdict, and the orchestrator must not read that as "down".
|
|
@@ -3657,7 +3679,7 @@ class LocalMiniappRuntime {
|
|
|
3657
3679
|
// Teams configuration problem.
|
|
3658
3680
|
const defaultNetworkStartedAt = Date.now();
|
|
3659
3681
|
try {
|
|
3660
|
-
const network = await acsMeetingService.
|
|
3682
|
+
const network = await acsMeetingService.awaitDefaultNetworkAfterHotspot();
|
|
3661
3683
|
softapTrace("softap_teardown_step", {
|
|
3662
3684
|
attempt: attempt.id,
|
|
3663
3685
|
step: "awaitValidatedDefaultNetwork",
|
|
@@ -3732,6 +3754,8 @@ class LocalMiniappRuntime {
|
|
|
3732
3754
|
failures: failures.join("; "),
|
|
3733
3755
|
endRefused: Boolean(endFailure),
|
|
3734
3756
|
});
|
|
3757
|
+
attempt.releaseHotspot?.();
|
|
3758
|
+
attempt.releaseHotspot = undefined;
|
|
3735
3759
|
if (endFailure)
|
|
3736
3760
|
throw endFailure;
|
|
3737
3761
|
}
|
|
@@ -3852,7 +3876,7 @@ class LocalMiniappRuntime {
|
|
|
3852
3876
|
}
|
|
3853
3877
|
async handleMeetingUpdateVideoSource(packageName, payload, requestId) {
|
|
3854
3878
|
const videoSource = payload.videoSource;
|
|
3855
|
-
const whepUrl = videoSource?.type === "whep" ? videoSource.url ?? "" : "";
|
|
3879
|
+
const whepUrl = videoSource?.type === "whep" ? (videoSource.url ?? "") : "";
|
|
3856
3880
|
if (!whepUrl) {
|
|
3857
3881
|
this.sendResult(packageName, requestId, false, undefined, {
|
|
3858
3882
|
code: MiniappErrorCode.INVALID_ARGUMENT,
|