Skip to content

Immersive TWA: SurfaceSyncGroup timeout — WebXRCustomTabActivity never renders on Quest 2 (Browser v144) #24

@devasur

Description

@devasur

Summary

TWA packaged with horizonOSAppMode: 'immersive' launches WebXRCustomTabActivity in the VR compositor, but the web content never renders. The Horizon OS interstitial (pulsating dots) persists indefinitely, followed by "App name unavailable" error. The same TWA works correctly with horizonOSAppMode: '2D'.

Environment

Field Value
Headset Meta Quest 2
Firmware Android 14 (SDK 34), Build UP1A.231005.007.A1
Quest Browser 144.2.0.29.52.912191064
Bubblewrap Meta fork v1.22.3 (latest main)
androidbrowserhelper 2.5.0
horizonplatformsdk 1.1.0
Host OS Linux (Ubuntu, x86_64)
JDK OpenJDK 17.0.18

Configuration

twa-manifest.json (relevant fields):

{
  "packageId": "in.walkinto.xr",
  "host": "walkinto.in",
  "startUrl": "/viewer/xr/vr",
  "horizonOSAppMode": "immersive",
  "isMetaQuest": true,
  "minSdkVersion": 23
}

AndroidManifest.xml includes:

  • <category android:name="com.oculus.intent.category.VR" />
  • <meta-data android:name="horizonos.pwa.APP_MODE" android:value="immersive" />
  • <meta-data android:name="com.meta.horizon.platform.ovr.OCULUS_APP_ID" android:value="\ 26873319878939517" />
  • <uses-permission android:name="android.permission.INTERNET" />
  • Digital Asset Links verified (assetlinks.json live, fingerprints match)

Reproduction Steps

  1. bubblewrap build with horizonOSAppMode: 'immersive'
  2. adb install app-release-signed.apk (or upload to RC channel via ovr-platform-util)
  3. Launch app from Quest Library → Unknown Sources (sideload) or from Store (RC channel)

Observed Behaviour

  1. LauncherActivity starts → Horizon OS recognises it as immersive:
    VolumetricContentMonitor: Switching to immersive app. taskId: 3
    
  2. CustomTabsService binding fails (expected on Quest):
    W ActivityManager: Unable to start service Intent { act=android.support.customtabs.action.CustomTabsService pkg=com.oculus.browser } U=0: not found
    
  3. After ~12s delay, WebXRCustomTabActivity starts:
    I ActivityTaskManager: START u0 {act=android.intent.action.VIEW dat=https://walkinto.in/viewer/xr/vr pkg=com.oculus.browser cmp=com.oculus.browser/.WebXRCustomTabActivity}
    
  4. Activity gains focus then immediately loses it and stops:
    cr_XRActivity: onResume! ... windowHasFocus? false
    cr_XRActivity: onWindowFocusChanged! ... true
    cr_XRActivity: onWindowFocusChanged! ... false    ← instant loss
    cr_XRActivity: onPause! ...
    cr_XRActivity: onStop! ...
    
  5. SurfaceSyncGroup times out after 2 seconds:
    E SurfaceSyncGroup: Failed to receive transaction ready in 2000ms. Marking SurfaceSyncGroup(wmsSync-VRI[WebXRCustomTabActivity]#26) as ready
    E SurfaceSyncGroup: Failed to receive transaction ready in 2000ms. Marking SurfaceSyncGroup(VRI[WebXRCustomTabActivity]#27) as ready
    E SurfaceSyncGroup: Failed to receive transaction ready in 2000ms. Marking SurfaceSyncGroup(SurfaceView[com.oculus.browser/com.oculus.browser.WebXRCustomTabActivity]#25) as ready
    
  6. User sees Horizon loading dots indefinitely, then "App name unavailable" toast.

Key Finding: Page IS Loaded

Despite the blank screen, Chrome DevTools (chrome://inspect) shows the page is fully loaded and rendered:

$ curl -s http://localhost:9222/json
[{ "title": "WalkInto XR", "url": "https://walkinto.in/viewer/xr/vr", "type": "page" }]

Evaluating JS on the page returns full DOM with all content (lobby UI, tours, navigation). The page renders correctly — the issue is that the VR compositor surface never connects to display it.

2D Mode Works

Switching to horizonOSAppMode: '2D' with com.oculus.intent.category.2D — the exact same URL loads and renders correctly as a flat panel in Horizon OS. This confirms the web content, Digital Asset Links, INTERNET permission, and TWA binding all work. Only the immersive surface pipeline fails.

Tested Both Sideload and Store (RC Channel)

  • Sideloaded via adb install: same SurfaceSyncGroup timeout
  • Uploaded to RC channel via ovr-platform-util upload-quest-build: same SurfaceSyncGroup timeout
  • Real OCULUS_APP_ID set in manifest: same result

Questions

  1. Is immersive TWA mode (horizonOSAppMode: 'immersive') currently functional on Quest Browser v144?
  2. Are there additional prerequisites beyond what bubblewrap generates (e.g., Horizon Store approval, specific browser flags, developer mode settings)?
  3. Is the WebXRCustomTabActivity surface pipeline expected to work for sideloaded/RC-channel apps, or only for production store-distributed apps?
  4. The ovr-platform-util create-pwa command requires a pwa-template.apk — is there a current version of this template available? The one in the pwabuilder-oculus repo (June 2022) is incompatible with current apktool versions.

Workaround

Using horizonOSAppMode: '2D' as a working baseline. Users can still enter WebXR immersive sessions from within the 2D panel via navigator.xr.requestSession('immersive-vr').

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions